diff options
| author | Stefan Boberg <[email protected]> | 2023-05-15 18:53:38 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-05-15 18:53:38 +0200 |
| commit | 3fb79e25865c3bafa9156c2db767ddc16f5019f3 (patch) | |
| tree | d476cf8c3b05793642b1a029171d76dfcf9f092d /src | |
| parent | added trace::DescribeSession to TraceInit (diff) | |
| download | zen-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.cpp | 36 | ||||
| -rw-r--r-- | src/zen/cmds/projectstore.cpp | 72 | ||||
| -rw-r--r-- | src/zen/cmds/rpcreplay.cpp | 27 | ||||
| -rw-r--r-- | src/zen/cmds/scrub.cpp | 57 | ||||
| -rw-r--r-- | src/zen/zen.cpp | 29 | ||||
| -rw-r--r-- | src/zen/zen.h | 1 | ||||
| -rw-r--r-- | src/zenhttp/httpclient.cpp | 46 | ||||
| -rw-r--r-- | src/zenhttp/include/zenhttp/httpclient.h | 9 |
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); |