aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2023-05-15 18:53:38 +0200
committerGitHub <[email protected]>2023-05-15 18:53:38 +0200
commit3fb79e25865c3bafa9156c2db767ddc16f5019f3 (patch)
treed476cf8c3b05793642b1a029171d76dfcf9f092d /src
parentadded trace::DescribeSession to TraceInit (diff)
downloadzen-3fb79e25865c3bafa9156c2db767ddc16f5019f3.tar.xz
zen-3fb79e25865c3bafa9156c2db767ddc16f5019f3.zip
Better defaults for zen cli (#302)
added ZenCmdBase::ResolveTargetHostSpec - this is a helper which provides better default behaviour for commands which interact with a local zen server instance. more specifically it picks a default based on which processes are actually running on the local machine this change also wires up the Scrub command along with some required HttpClient improvements
Diffstat (limited to 'src')
-rw-r--r--src/zen/cmds/cache.cpp36
-rw-r--r--src/zen/cmds/projectstore.cpp72
-rw-r--r--src/zen/cmds/rpcreplay.cpp27
-rw-r--r--src/zen/cmds/scrub.cpp57
-rw-r--r--src/zen/zen.cpp29
-rw-r--r--src/zen/zen.h1
-rw-r--r--src/zenhttp/httpclient.cpp46
-rw-r--r--src/zenhttp/include/zenhttp/httpclient.h9
8 files changed, 254 insertions, 23 deletions
diff --git a/src/zen/cmds/cache.cpp b/src/zen/cmds/cache.cpp
index 0de3f8bc3..15c24f9ee 100644
--- a/src/zen/cmds/cache.cpp
+++ b/src/zen/cmds/cache.cpp
@@ -17,7 +17,7 @@ ZEN_THIRD_PARTY_INCLUDES_END
DropCommand::DropCommand()
{
m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>");
+ m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
m_Options.add_option("", "n", "namespace", "Namespace name", cxxopts::value(m_NamespaceName), "<namespacename>");
m_Options.add_option("", "b", "bucket", "Bucket name", cxxopts::value(m_BucketName), "<bucketname>");
m_Options.parse_positional({"namespace", "bucket"});
@@ -35,6 +35,13 @@ DropCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
return 0;
}
+ m_HostName = ResolveTargetHostSpec(m_HostName);
+
+ if (m_HostName.empty())
+ {
+ throw zen::OptionParseException("unable to resolve server specification");
+ }
+
if (m_NamespaceName.empty())
{
throw zen::OptionParseException("Drop command requires a namespace");
@@ -76,7 +83,7 @@ DropCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
CacheInfoCommand::CacheInfoCommand()
{
m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>");
+ m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
m_Options.add_option("", "n", "namespace", "Namespace name", cxxopts::value(m_NamespaceName), "<namespacename>");
m_Options.add_option("", "b", "bucket", "Bucket name", cxxopts::value(m_BucketName), "<bucketname>");
m_Options.parse_positional({"namespace", "bucket"});
@@ -94,6 +101,13 @@ CacheInfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
return 0;
}
+ m_HostName = ResolveTargetHostSpec(m_HostName);
+
+ if (m_HostName.empty())
+ {
+ throw zen::OptionParseException("unable to resolve server specification");
+ }
+
cpr::Session Session;
Session.SetHeader(cpr::Header{{"Accept", "application/json"}});
if (m_HostName.empty())
@@ -136,7 +150,7 @@ CacheInfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
CacheStatsCommand::CacheStatsCommand()
{
m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>");
+ m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
}
CacheStatsCommand::~CacheStatsCommand() = default;
@@ -151,6 +165,13 @@ CacheStatsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv
return 0;
}
+ m_HostName = ResolveTargetHostSpec(m_HostName);
+
+ if (m_HostName.empty())
+ {
+ throw zen::OptionParseException("unable to resolve server specification");
+ }
+
cpr::Session Session;
Session.SetUrl({fmt::format("{}/stats/z$", m_HostName)});
Session.SetHeader(cpr::Header{{"Accept", "application/json"}});
@@ -179,7 +200,7 @@ CacheStatsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv
CacheDetailsCommand::CacheDetailsCommand()
{
m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>");
+ m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
m_Options.add_option("", "c", "csv", "Info on csv format", cxxopts::value(m_CSV), "<csv>");
m_Options.add_option("", "d", "details", "Get detailed information about records", cxxopts::value(m_Details), "<details>");
m_Options.add_option("",
@@ -205,6 +226,13 @@ CacheDetailsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** ar
return 0;
}
+ m_HostName = ResolveTargetHostSpec(m_HostName);
+
+ if (m_HostName.empty())
+ {
+ throw zen::OptionParseException("unable to resolve server specification");
+ }
+
cpr::Session Session;
cpr::Parameters Parameters;
if (m_Details)
diff --git a/src/zen/cmds/projectstore.cpp b/src/zen/cmds/projectstore.cpp
index 9db7a4841..c30383d1b 100644
--- a/src/zen/cmds/projectstore.cpp
+++ b/src/zen/cmds/projectstore.cpp
@@ -31,7 +31,7 @@ const std::string DefaultCloudAccessTokenEnvVariableName(
DropProjectCommand::DropProjectCommand()
{
m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>");
+ m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
m_Options.add_option("", "p", "project", "Project name", cxxopts::value(m_ProjectName), "<projectid>");
m_Options.add_option("", "o", "oplog", "Oplog name", cxxopts::value(m_OplogName), "<oplogid>");
m_Options.parse_positional({"project", "oplog"});
@@ -49,6 +49,13 @@ DropProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
return 0;
}
+ m_HostName = ResolveTargetHostSpec(m_HostName);
+
+ if (m_HostName.empty())
+ {
+ throw zen::OptionParseException("unable to resolve server specification");
+ }
+
if (m_ProjectName.empty())
{
throw zen::OptionParseException("Drop command requires a project");
@@ -91,7 +98,7 @@ DropProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
ProjectInfoCommand::ProjectInfoCommand()
{
m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>");
+ m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
m_Options.add_option("", "p", "project", "Project name", cxxopts::value(m_ProjectName), "<projectid>");
m_Options.add_option("", "o", "oplog", "Oplog name", cxxopts::value(m_OplogName), "<oplogid>");
m_Options.parse_positional({"project", "oplog"});
@@ -109,6 +116,13 @@ ProjectInfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
return 0;
}
+ m_HostName = ResolveTargetHostSpec(m_HostName);
+
+ if (m_HostName.empty())
+ {
+ throw zen::OptionParseException("unable to resolve server specification");
+ }
+
cpr::Session Session;
Session.SetHeader(cpr::Header{{"Accept", "application/json"}});
if (m_ProjectName.empty())
@@ -153,7 +167,7 @@ ProjectInfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
CreateProjectCommand::CreateProjectCommand()
{
m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>");
+ m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
m_Options.add_option("", "p", "project", "Project name", cxxopts::value(m_ProjectId), "<projectid>");
m_Options.add_option("", "", "rootdir", "Absolute path to root directory", cxxopts::value(m_RootDir), "<root>");
m_Options.add_option("", "", "enginedir", "Absolute path to engine root directory", cxxopts::value(m_EngineRootDir), "<engineroot>");
@@ -176,6 +190,13 @@ CreateProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a
return 0;
}
+ m_HostName = ResolveTargetHostSpec(m_HostName);
+
+ if (m_HostName.empty())
+ {
+ throw zen::OptionParseException("unable to resolve server specification");
+ }
+
cpr::Session Session;
Session.SetHeader(cpr::Header{{"Accept", "application/json"}});
@@ -216,7 +237,7 @@ CreateProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a
CreateOplogCommand::CreateOplogCommand()
{
m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>");
+ m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
m_Options.add_option("", "p", "project", "Project name", cxxopts::value(m_ProjectId), "<projectid>");
m_Options.add_option("", "o", "oplog", "Oplog name", cxxopts::value(m_OplogId), "<oplogid>");
m_Options.add_option("", "", "gcpath", "Absolute path to oplog lifetime marker file", cxxopts::value(m_GcPath), "<path>");
@@ -237,6 +258,13 @@ CreateOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
return 0;
}
+ m_HostName = ResolveTargetHostSpec(m_HostName);
+
+ if (m_HostName.empty())
+ {
+ throw zen::OptionParseException("unable to resolve server specification");
+ }
+
cpr::Session Session;
Session.SetHeader(cpr::Header{{"Accept", "application/json"}});
@@ -285,7 +313,7 @@ CreateOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
ExportOplogCommand::ExportOplogCommand()
{
m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>");
+ m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
m_Options.add_option("", "p", "project", "Project name", cxxopts::value(m_ProjectName), "<projectid>");
m_Options.add_option("", "o", "oplog", "Oplog name", cxxopts::value(m_OplogName), "<oplogid>");
m_Options.add_option("", "", "maxblocksize", "Max size for bundled attachments", cxxopts::value(m_MaxBlockSize), "<blocksize>");
@@ -354,6 +382,13 @@ ExportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
return 0;
}
+ m_HostName = ResolveTargetHostSpec(m_HostName);
+
+ if (m_HostName.empty())
+ {
+ throw zen::OptionParseException("unable to resolve server specification");
+ }
+
if (m_ProjectName.empty())
{
ZEN_ERROR("Project name must be given");
@@ -560,7 +595,7 @@ ExportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
ImportOplogCommand::ImportOplogCommand()
{
m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>");
+ m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
m_Options.add_option("", "p", "project", "Project name", cxxopts::value(m_ProjectName), "<projectid>");
m_Options.add_option("", "o", "oplog", "Oplog name", cxxopts::value(m_OplogName), "<oplogid>");
m_Options.add_option("", "", "maxblocksize", "Max size for bundled attachments", cxxopts::value(m_MaxBlockSize), "<blocksize>");
@@ -611,6 +646,13 @@ ImportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
return 0;
}
+ m_HostName = ResolveTargetHostSpec(m_HostName);
+
+ if (m_HostName.empty())
+ {
+ throw zen::OptionParseException("unable to resolve server specification");
+ }
+
if (m_ProjectName.empty())
{
ZEN_ERROR("Project name must be given");
@@ -785,7 +827,7 @@ ImportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
ProjectStatsCommand::ProjectStatsCommand()
{
m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>");
+ m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
}
ProjectStatsCommand::~ProjectStatsCommand() = default;
@@ -800,6 +842,13 @@ ProjectStatsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** ar
return 0;
}
+ m_HostName = ResolveTargetHostSpec(m_HostName);
+
+ if (m_HostName.empty())
+ {
+ throw zen::OptionParseException("unable to resolve server specification");
+ }
+
cpr::Session Session;
Session.SetUrl({fmt::format("{}/stats/prj", m_HostName)});
Session.SetHeader(cpr::Header{{"Accept", "application/json"}});
@@ -828,7 +877,7 @@ ProjectStatsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** ar
ProjectDetailsCommand::ProjectDetailsCommand()
{
m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>");
+ m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
m_Options.add_option("", "c", "csv", "Output in CSV format (default is JSon)", cxxopts::value(m_CSV), "<csv>");
m_Options.add_option("", "d", "details", "Detailed info on opslog", cxxopts::value(m_Details), "<details>");
m_Options.add_option("", "o", "opdetails", "Details info on oplog body", cxxopts::value(m_OpDetails), "<opdetails>");
@@ -855,6 +904,13 @@ ProjectDetailsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char**
return 0;
}
+ m_HostName = ResolveTargetHostSpec(m_HostName);
+
+ if (m_HostName.empty())
+ {
+ throw zen::OptionParseException("unable to resolve server specification");
+ }
+
cpr::Session Session;
cpr::Parameters Parameters;
if (m_OpDetails)
diff --git a/src/zen/cmds/rpcreplay.cpp b/src/zen/cmds/rpcreplay.cpp
index b99c63962..349025791 100644
--- a/src/zen/cmds/rpcreplay.cpp
+++ b/src/zen/cmds/rpcreplay.cpp
@@ -28,7 +28,7 @@ using namespace std::literals;
RpcStartRecordingCommand::RpcStartRecordingCommand()
{
m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>");
+ m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
m_Options.add_option("", "p", "path", "Recording file path", cxxopts::value(m_RecordingPath), "<path>");
m_Options.parse_positional("path");
@@ -45,6 +45,13 @@ RpcStartRecordingCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char
return 0;
}
+ m_HostName = ResolveTargetHostSpec(m_HostName);
+
+ if (m_HostName.empty())
+ {
+ throw zen::OptionParseException("unable to resolve server specification");
+ }
+
if (m_RecordingPath.empty())
{
throw zen::OptionParseException("Rpc start recording command requires a path");
@@ -63,7 +70,7 @@ RpcStartRecordingCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char
RpcStopRecordingCommand::RpcStopRecordingCommand()
{
m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>");
+ m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
}
RpcStopRecordingCommand::~RpcStopRecordingCommand() = default;
@@ -78,6 +85,13 @@ RpcStopRecordingCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char*
return 0;
}
+ m_HostName = ResolveTargetHostSpec(m_HostName);
+
+ if (m_HostName.empty())
+ {
+ throw zen::OptionParseException("unable to resolve server specification");
+ }
+
cpr::Session Session;
Session.SetUrl(fmt::format("{}/z$/exec$/stop-recording"sv, m_HostName));
cpr::Response Response = Session.Post();
@@ -90,7 +104,7 @@ RpcStopRecordingCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char*
RpcReplayCommand::RpcReplayCommand()
{
m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>");
+ m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
m_Options.add_option("", "p", "path", "Recording file path", cxxopts::value(m_RecordingPath), "<path>");
m_Options.add_option("",
"w",
@@ -166,6 +180,13 @@ RpcReplayCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
return 0;
}
+ m_HostName = ResolveTargetHostSpec(m_HostName);
+
+ if (m_HostName.empty())
+ {
+ throw zen::OptionParseException("unable to resolve server specification");
+ }
+
if (m_RecordingPath.empty())
{
throw zen::OptionParseException("Rpc replay command requires a path");
diff --git a/src/zen/cmds/scrub.cpp b/src/zen/cmds/scrub.cpp
index 27ff5e0ac..c3ca0ff1e 100644
--- a/src/zen/cmds/scrub.cpp
+++ b/src/zen/cmds/scrub.cpp
@@ -2,6 +2,7 @@
#include "scrub.h"
#include <zencore/logging.h>
+#include <zenhttp/httpclient.h>
#include <zenhttp/httpcommon.h>
ZEN_THIRD_PARTY_INCLUDES_START
@@ -15,7 +16,7 @@ namespace zen {
ScrubCommand::ScrubCommand()
{
m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>");
+ m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
}
ScrubCommand::~ScrubCommand() = default;
@@ -23,9 +24,41 @@ ScrubCommand::~ScrubCommand() = default;
int
ScrubCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
{
- ZEN_UNUSED(GlobalOptions, argc, argv);
+ ZEN_UNUSED(GlobalOptions);
+
+ if (!ParseOptions(argc, argv))
+ {
+ return 0;
+ }
+
+ m_HostName = ResolveTargetHostSpec(m_HostName);
+
+ if (m_HostName.empty())
+ {
+ throw OptionParseException("unable to resolve server specification");
+ }
+
+ zen::HttpClient Http(m_HostName);
+
+ if (zen::HttpClient::Response Response = Http.Post("/admin/scrub"sv))
+ {
+ ZEN_CONSOLE("OK: {}", Response.ToText());
+
+ return 0;
+ }
+ else if (int StatusCode = (int)Response.StatusCode)
+ {
+ ZEN_ERROR("scrub start failed: {}: {} ({})",
+ (int)Response.StatusCode,
+ ReasonStringForHttpResultCode((int)Response.StatusCode),
+ Response.AsText());
+ }
+ else
+ {
+ ZEN_ERROR("scrub start failed: {}", Response.AsText());
+ }
- return 0;
+ return 1;
}
//////////////////////////////////////////////////////////////////////////
@@ -33,7 +66,7 @@ ScrubCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
GcCommand::GcCommand()
{
m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>");
+ m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
m_Options.add_option("",
"s",
"smallobjects",
@@ -68,6 +101,13 @@ GcCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
return 0;
}
+ m_HostName = ResolveTargetHostSpec(m_HostName);
+
+ if (m_HostName.empty())
+ {
+ throw OptionParseException("unable to resolve server specification");
+ }
+
cpr::Parameters Params;
if (m_SmallObjects)
{
@@ -110,7 +150,7 @@ GcCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
GcStatusCommand::GcStatusCommand()
{
m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>");
+ m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
}
GcStatusCommand::~GcStatusCommand()
@@ -127,6 +167,13 @@ GcStatusCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
return 0;
}
+ m_HostName = ResolveTargetHostSpec(m_HostName);
+
+ if (m_HostName.empty())
+ {
+ throw OptionParseException("unable to resolve server specification");
+ }
+
cpr::Session Session;
Session.SetHeader(cpr::Header{{"Accept", "application/json"}});
Session.SetUrl({fmt::format("{}/admin/gc", m_HostName)});
diff --git a/src/zen/zen.cpp b/src/zen/zen.cpp
index 7f0549d42..3ae62e29d 100644
--- a/src/zen/zen.cpp
+++ b/src/zen/zen.cpp
@@ -23,8 +23,8 @@
#include <zencore/logging.h>
#include <zencore/scopeguard.h>
#include <zencore/string.h>
-
#include <zenhttp/httpcommon.h>
+#include <zenutil/zenserverprocess.h>
#if ZEN_WITH_TESTS
# define ZEN_TEST_WITH_RUNNER 1
@@ -208,6 +208,7 @@ main(int argc, char** argv)
RpcReplayCommand RpcReplayCmd;
RpcStartRecordingCommand RpcStartRecordingCmd;
RpcStopRecordingCommand RpcStopRecordingCmd;
+ ScrubCommand ScrubCmd;
StatusCommand StatusCmd;
TopCommand TopCmd;
UpCommand UpCmd;
@@ -248,6 +249,7 @@ main(int argc, char** argv)
{"rpc-record-replay", &RpcReplayCmd, "Stops recording of cache rpc requests on a host"},
{"rpc-record-start", &RpcStartRecordingCmd, "Replays a previously recorded session of rpc requests"},
{"rpc-record-stop", &RpcStopRecordingCmd, "Starts recording of cache rpc requests on a host"},
+ {"scrub", &ScrubCmd, "Scrub zen storage (verify data integrity)"},
{"status", &StatusCmd, "Show zen status"},
{"top", &TopCmd, "Monitor zen server activity"},
{"up", &UpCmd, "Bring zen server up"},
@@ -435,3 +437,28 @@ main(int argc, char** argv)
return 0;
}
+
+std::string
+ZenCmdBase::ResolveTargetHostSpec(const std::string& InHostSpec)
+{
+ if (InHostSpec.empty())
+ {
+ zen::ZenServerState Servers;
+
+ if (Servers.InitializeReadOnly())
+ {
+ std::string ResolvedSpec = InHostSpec;
+
+ Servers.Snapshot([&](const zen::ZenServerState::ZenServerEntry& Entry) {
+ if (ResolvedSpec.empty())
+ {
+ ResolvedSpec = fmt::format("http://localhost:{}", Entry.EffectiveListenPort);
+ }
+ });
+
+ return ResolvedSpec;
+ }
+ }
+
+ return InHostSpec;
+}
diff --git a/src/zen/zen.h b/src/zen/zen.h
index f6c2d8ff0..91273fb5e 100644
--- a/src/zen/zen.h
+++ b/src/zen/zen.h
@@ -39,4 +39,5 @@ public:
bool ParseOptions(int argc, char** argv);
static std::string FormatHttpResponse(const cpr::Response& Response);
static int MapHttpToCommandReturnCode(const cpr::Response& Response);
+ static std::string ResolveTargetHostSpec(const std::string& InHostSpec);
};
diff --git a/src/zenhttp/httpclient.cpp b/src/zenhttp/httpclient.cpp
index 891ada83e..fa290ef52 100644
--- a/src/zenhttp/httpclient.cpp
+++ b/src/zenhttp/httpclient.cpp
@@ -83,6 +83,15 @@ CommonResponse(cpr::Response&& HttpResponse)
{
const HttpResponseCode WorkResponseCode = HttpResponseCode(HttpResponse.status_code);
+ if (HttpResponse.status_code == 0)
+ {
+ // Client side failure code
+
+ return HttpClient::Response{
+ .StatusCode = WorkResponseCode,
+ .ResponsePayload = IoBufferBuilder::MakeCloneFromMemory(HttpResponse.error.message.data(), HttpResponse.error.message.size())};
+ }
+
if (WorkResponseCode == HttpResponseCode::NoContent || HttpResponse.text.empty())
{
return HttpClient::Response{.StatusCode = WorkResponseCode};
@@ -310,6 +319,13 @@ HttpClient::Delete(std::string_view Url)
}
HttpClient::Response
+HttpClient::Post(std::string_view Url)
+{
+ Impl::Session Sess = m_Impl->AllocSession(m_BaseUri, Url);
+ return CommonResponse(Sess->Post());
+}
+
+HttpClient::Response
HttpClient::Post(std::string_view Url, const IoBuffer& Payload)
{
Impl::Session Sess = m_Impl->AllocSession(m_BaseUri, Url);
@@ -381,6 +397,36 @@ HttpClient::Response::AsText()
return {};
}
+std::string
+HttpClient::Response::ToText()
+{
+ if (!ResponsePayload)
+ return {};
+
+ switch (ResponsePayload.GetContentType())
+ {
+ case ZenContentType::kCbObject:
+ {
+ zen::ExtendableStringBuilder<1024> ObjStr;
+ zen::CbObject Object{SharedBuffer(ResponsePayload)};
+ zen::CompactBinaryToJson(Object, ObjStr);
+ return ObjStr.ToString();
+ }
+ break;
+
+ case ZenContentType::kCSS:
+ case ZenContentType::kHTML:
+ case ZenContentType::kJavaScript:
+ case ZenContentType::kJSON:
+ case ZenContentType::kText:
+ case ZenContentType::kYAML:
+ return std::string{AsText()};
+
+ default:
+ return "<unhandled content format>";
+ }
+}
+
bool
HttpClient::Response::IsSuccess() const noexcept
{
diff --git a/src/zenhttp/include/zenhttp/httpclient.h b/src/zenhttp/include/zenhttp/httpclient.h
index edf3bf773..9f08835d3 100644
--- a/src/zenhttp/include/zenhttp/httpclient.h
+++ b/src/zenhttp/include/zenhttp/httpclient.h
@@ -35,13 +35,18 @@ public:
// validate that the content type or content itself makes sense as a string.
std::string_view AsText();
- bool IsSuccess() const noexcept;
- inline explicit operator bool() const noexcept { return IsSuccess(); }
+ // Return text representation of the payload. Formats into JSON for structured
+ // objects, returns text as-is for text types like Text, JSON, HTML etc
+ std::string ToText();
+
+ bool IsSuccess() const noexcept;
+ inline operator bool() const noexcept { return IsSuccess(); }
};
[[nodiscard]] Response Put(std::string_view Url, const IoBuffer& Payload);
[[nodiscard]] Response Get(std::string_view Url);
[[nodiscard]] Response Delete(std::string_view Url);
+ [[nodiscard]] Response Post(std::string_view Url);
[[nodiscard]] Response Post(std::string_view Url, const IoBuffer& Payload);
[[nodiscard]] Response Post(std::string_view Url, CbObject Payload);
[[nodiscard]] Response Post(std::string_view Url, CbPackage Payload);