diff options
| author | Dan Engelbrecht <[email protected]> | 2025-09-05 13:02:27 +0200 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2025-09-05 13:02:27 +0200 |
| commit | 45b0307d42b22e04cee63467a8fdb898a2d8d552 (patch) | |
| tree | 905b3eb62af89269be5b15f4c407d900ce86f7f3 /src | |
| parent | Avoid mutating executable paths when copying files during full service instal... (diff) | |
| download | zen-45b0307d42b22e04cee63467a8fdb898a2d8d552.tar.xz zen-45b0307d42b22e04cee63467a8fdb898a2d8d552.zip | |
refactor zen command return value handling (#487)
- Improvement: Use consistent language for command line argument parsing errors
- Improvement: Changed zen command parsing errors to output help first and error last to make it easier to spot the error
- Improvement: Refactor zen command return codes to conform to valid Linux range (0-255)
kSuccess = 0,
kOtherError = 1,
kBadInput = 2,
kOutOfMemory = 16,
kOutOfDisk = 17,
kAssertError = 70,
kHttpOtherClientError = 80,
kHttpCantConnectError = 81,
kHttpNotFound = 66, // NotFound(404)
kHttpUnauthorized = 77, // Unauthorized(401),
kHttpSLLError = 82,
kHttpForbidden = 83, // Forbidden(403)
kHttpTimeout = 84, // RequestTimeout(408)
kHttpConflict = 85, // Conflict(409)
kHttpNoHost = 86,
kHttpOtherServerError = 90,
kHttpInternalServerError = 91, // InternalServerError(500)
kHttpServiceUnavailable = 69, // ServiceUnavailable(503)
kHttpBadGateway = 92, // BadGateway(502)
kHttpGatewayTimeout = 93, // GatewayTimeout(504)
Diffstat (limited to 'src')
47 files changed, 972 insertions, 1083 deletions
diff --git a/src/zen/cmds/admin_cmd.cpp b/src/zen/cmds/admin_cmd.cpp index dca6da6c4..f0a14691b 100644 --- a/src/zen/cmds/admin_cmd.cpp +++ b/src/zen/cmds/admin_cmd.cpp @@ -4,6 +4,7 @@ #include <zencore/basicfile.h> #include <zencore/filesystem.h> +#include <zencore/fmtutils.h> #include <zencore/logging.h> #include <zenhttp/formatters.h> #include <zenhttp/httpclient.h> @@ -28,21 +29,21 @@ ScrubCommand::ScrubCommand() ScrubCommand::~ScrubCommand() = default; -int +void ScrubCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } HttpClient Http(m_HostName); @@ -52,19 +53,11 @@ ScrubCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (HttpClient::Response Response = Http.Post("/admin/scrub"sv, /* headers */ HttpClient::KeyValueMap{}, Params)) { ZEN_CONSOLE("Scrub started OK: {}", Response.ToText()); - - return 0; - } - else if (int StatusCode = (int)Response.StatusCode) - { - ZEN_CONSOLE_ERROR("Scrub start failed: {}: {} ({})", StatusCode, ReasonStringForHttpResultCode(StatusCode), Response.ToText()); } else { - ZEN_CONSOLE_ERROR("Scrub start failed: {}", Response.ToText()); + Response.ThrowError("Scrub start failed"); } - - return 1; } ////////////////////////////////////////////////////////////////////////// @@ -160,113 +153,115 @@ GcCommand::~GcCommand() { } -int +void GcCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } - cpr::Parameters Params; - Params.Add({"smallobjects", m_SmallObjects ? "true" : "false"}); + HttpClient::KeyValueMap Params; + Params.Entries.insert({"smallobjects", m_SmallObjects ? "true" : "false"}); if (m_MaxCacheDuration != 0) { - Params.Add({"maxcacheduration", fmt::format("{}", m_MaxCacheDuration)}); + Params.Entries.insert({"maxcacheduration", fmt::format("{}", m_MaxCacheDuration)}); } if (m_DiskSizeSoftLimit != 0) { - Params.Add({"disksizesoftlimit", fmt::format("{}", m_DiskSizeSoftLimit)}); + Params.Entries.insert({"disksizesoftlimit", fmt::format("{}", m_DiskSizeSoftLimit)}); } - Params.Add({"skipcid", m_SkipCid ? "true" : "false"}); - Params.Add({"skipdelete", m_SkipDelete ? "true" : "false"}); + Params.Entries.insert({"skipcid", m_SkipCid ? "true" : "false"}); + Params.Entries.insert({"skipdelete", m_SkipDelete ? "true" : "false"}); if (m_ForceUseGCV1) { - throw OptionParseException("usegcv1 is deprecated and can no longer be used"); + throw OptionParseException("'--usegcv1' is deprecated and can no longer be used", m_Options.help()); } if (m_ForceUseGCV2) { - Params.Add({"forceusegcv2", "true"}); + Params.Entries.insert({"forceusegcv2", "true"}); } if (m_CompactBlockThreshold) { - Params.Add({"compactblockthreshold", fmt::format("{}", m_CompactBlockThreshold)}); + Params.Entries.insert({"compactblockthreshold", fmt::format("{}", m_CompactBlockThreshold)}); } IoHash LowRef = IoHash::Zero; if (!m_ReferenceHashLow.empty()) { if (m_ReferenceHashLow.length() != IoHash::StringLength) { - throw OptionParseException(fmt::format("reference-low must be a {} character hex string", IoHash::StringLength)); + throw OptionParseException(fmt::format("'--reference-low' ('{}') is malformed, it must be a {} character hex string", + m_ReferenceHashLow, + IoHash::StringLength), + m_Options.help()); + } + if (!IoHash::TryParse(m_ReferenceHashLow, LowRef)) + { + throw OptionParseException(fmt::format("'--reference-low' ('{}') is malformed", m_ReferenceHashLow), m_Options.help()); } - LowRef = IoHash::FromHexString(m_ReferenceHashLow); } IoHash HighRef = IoHash::Max; if (!m_ReferenceHashHigh.empty()) { if (m_ReferenceHashHigh.length() != IoHash::StringLength) { - throw OptionParseException(fmt::format("reference-high must be a {} character hex string", IoHash::StringLength)); + throw OptionParseException(fmt::format("''--reference-high' ('{}') is malformed, it must be a {} character hex string", + m_ReferenceHashHigh, + IoHash::StringLength), + m_Options.help()); + } + if (!IoHash::TryParse(m_ReferenceHashHigh, HighRef)) + { + throw OptionParseException(fmt::format("'--reference-high' ('{}') is malformed", m_ReferenceHashHigh), m_Options.help()); } - HighRef = IoHash::FromHexString(m_ReferenceHashHigh); } if (HighRef < LowRef) { - throw OptionParseException(fmt::format("invalid reference range, reference-high must be higher value than reference-low")); + throw OptionParseException( + fmt::format("'--reference-high' ('{}') is invalid, it must be a higher value than '--reference-low' ('{}')", + m_ReferenceHashHigh, + m_ReferenceHashLow), + m_Options.help()); } if (!m_ReferenceHashLow.empty() || !m_ReferenceHashHigh.empty()) { - Params.Add({"referencehashlow", LowRef.ToHexString()}); - Params.Add({"referencehashhigh", HighRef.ToHexString()}); + Params.Entries.insert({"referencehashlow", LowRef.ToHexString()}); + Params.Entries.insert({"referencehashhigh", HighRef.ToHexString()}); } - Params.Add({"verbose", m_Verbose ? "true" : "false"}); - Params.Add({"singlethreaded", m_SingleThreaded ? "true" : "false"}); + Params.Entries.insert({"verbose", m_Verbose ? "true" : "false"}); + Params.Entries.insert({"singlethreaded", m_SingleThreaded ? "true" : "false"}); if (m_StoreCacheAttachmentMetaData) { - Params.Add({"storecacheattachmentmetadata", m_StoreCacheAttachmentMetaData ? "true" : "false"}); + Params.Entries.insert({"storecacheattachmentmetadata", m_StoreCacheAttachmentMetaData ? "true" : "false"}); } if (m_StoreProjectAttachmentMetaData) { - Params.Add({"storeprojectattachmentmetadata", m_StoreProjectAttachmentMetaData ? "true" : "false"}); - } - Params.Add({"enablevalidation", m_EnableValidation ? "true" : "false"}); - - cpr::Session Session; - Session.SetHeader(cpr::Header{{"Accept", "application/json"}}); - Session.SetUrl({fmt::format("{}/admin/gc", m_HostName)}); - Session.SetParameters(Params); - - cpr::Response Result = Session.Post(); - - if (zen::IsHttpSuccessCode(Result.status_code)) - { - ZEN_CONSOLE("OK: {}", Result.text); - return 0; + Params.Entries.insert({"storeprojectattachmentmetadata", m_StoreProjectAttachmentMetaData ? "true" : "false"}); } + Params.Entries.insert({"enablevalidation", m_EnableValidation ? "true" : "false"}); - if (Result.status_code) + HttpClient Http(m_HostName); + if (HttpClient::Response Response = Http.Post("/admin/gc"sv, HttpClient::Accept(HttpContentType::kJSON), Params)) { - ZEN_CONSOLE_ERROR("GC start failed: {}: {} ({})", Result.status_code, Result.reason, Result.text); + ZEN_CONSOLE("OK: {}", Response.ToText()); } else { - ZEN_CONSOLE_ERROR("GC start failed: {}", Result.error.message); + Response.ThrowError("GC start failed"); } - - return 1; } GcStatusCommand::GcStatusCommand() @@ -280,49 +275,32 @@ GcStatusCommand::~GcStatusCommand() { } -int +void GcStatusCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } - cpr::Session Session; - Session.SetHeader(cpr::Header{{"Accept", "application/json"}}); - Session.SetUrl({fmt::format("{}/admin/gc", m_HostName)}); - if (m_Details) - { - Session.SetParameters({{"details", "true"}}); - } - - cpr::Response Result = Session.Get(); - - if (zen::IsHttpSuccessCode(Result.status_code)) - { - ZEN_CONSOLE("OK: {}", Result.text); - return 0; - } - - if (Result.status_code) + HttpClient Http(m_HostName); + if (HttpClient::Response Response = Http.Get("/admin/gc"sv, HttpClient::Accept(HttpContentType::kJSON))) { - ZEN_CONSOLE_ERROR("GC status failed: {}: {} ({})", Result.status_code, Result.reason, Result.text); + ZEN_CONSOLE("OK: {}", Response.ToText()); } else { - ZEN_CONSOLE_ERROR("GC status failed: {}", Result.error.message); + Response.ThrowError("Gc status failed"); } - - return 1; } GcStopCommand::GcStopCommand() @@ -335,48 +313,39 @@ GcStopCommand::~GcStopCommand() { } -int +void GcStopCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } - cpr::Session Session; - Session.SetUrl({fmt::format("{}/admin/gc-stop", m_HostName)}); - cpr::Response Result = Session.Post(); - - if (static_cast<HttpResponseCode>(Result.status_code) == HttpResponseCode::Accepted) - { - ZEN_CONSOLE("OK: {}", "Cancel request accepted"); - return 0; - } - else if (zen::IsHttpSuccessCode(Result.status_code)) - { - ZEN_CONSOLE("OK: {}", "No GC running"); - return 0; - } - - if (Result.status_code) + HttpClient Http(m_HostName); + if (HttpClient::Response Response = Http.Post("/admin/gc-stop"sv, HttpClient::Accept(HttpContentType::kJSON))) { - ZEN_CONSOLE_ERROR("GC status failed: {}: {} ({})", Result.status_code, Result.reason, Result.text); + if (Response.StatusCode == HttpResponseCode::Accepted) + { + ZEN_CONSOLE("OK: {}", "Cancel request accepted"); + } + else + { + ZEN_CONSOLE("OK: {}", "No GC running"); + } } else { - ZEN_CONSOLE_ERROR("GC status failed: {}", Result.error.message); + Response.ThrowError("Gc stop failed"); } - - return 1; } //////////////////////////////////////////// @@ -391,7 +360,7 @@ JobCommand::JobCommand() JobCommand::~JobCommand() = default; -int +void JobCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); @@ -400,14 +369,14 @@ JobCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } HttpClient Http(m_HostName); @@ -416,8 +385,7 @@ JobCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { if (m_JobId == 0) { - throw OptionParseException("Job id must be given"); - return 1; + throw OptionParseException("'--jobid' is required", m_Options.help()); } } std::string Url = m_JobId != 0 ? fmt::format("/admin/jobs/{}", m_JobId) : "/admin/jobs"; @@ -431,7 +399,6 @@ JobCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) else { Result.ThrowError("failed cancelling job"sv); - return 1; } } else if (HttpClient::Response Result = Http.Get(Url, HttpClient::Accept(ZenContentType::kJSON))) @@ -441,10 +408,7 @@ JobCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) else { Result.ThrowError("failed fetching job info"sv); - return 1; } - - return 0; } //////////////////////////////////////////// @@ -479,7 +443,7 @@ LoggingCommand::LoggingCommand() LoggingCommand::~LoggingCommand() = default; -int +void LoggingCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); @@ -488,14 +452,14 @@ LoggingCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } HttpClient Http(m_HostName); @@ -514,8 +478,8 @@ LoggingCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } else { - ZEN_CONSOLE_ERROR("Invalid value for parameter 'cache-write-log'. Use 'enable' or 'disable'"); - return 1; + throw OptionParseException(fmt::format("'--cache-write-log' ('{}') is invalid, use 'enable' or 'disable'", m_CacheWriteLog), + m_Options.help()); } } @@ -531,8 +495,8 @@ LoggingCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } else { - ZEN_CONSOLE_ERROR("Invalid value for parameter 'cache-access-log'. Use 'enable' or 'disable'"); - return 1; + throw OptionParseException(fmt::format("'--cache-access-log' ('{}') is invalid, use 'enable' or 'disable'", m_CacheAccessLog), + m_Options.help()); } } @@ -548,49 +512,37 @@ LoggingCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ZEN_CONSOLE("{}", Result.ToText()); const CbObject LogsResponse = Result.AsObject(); - auto CopyLog = [](std::string_view SourceName, std::string_view SourcePath, std::string_view TargetPath) -> bool { + auto CopyLog = [](std::string_view SourceName, std::string_view SourcePath, std::string_view TargetPath) { if (SourcePath.empty()) { - ZEN_CONSOLE_ERROR("Failed to retrieve {} log path", SourceName); - return false; + throw std::runtime_error(fmt::format("Failed to retrieve {} log path", SourceName)); } if (!CopyFile(SourcePath, TargetPath, {})) { - ZEN_CONSOLE_ERROR("Failed to copy {} log file {} to output file '{}'", SourceName, SourcePath, TargetPath); - return false; + throw std::runtime_error( + fmt::format("Failed to copy {} log file {} to output file '{}'", SourceName, SourcePath, TargetPath)); } - return true; }; if (!m_ServerLogTarget.empty()) { - if (!CopyLog("server", LogsResponse["Logfile"].AsString(), m_ServerLogTarget)) - { - return 1; - } + CopyLog("server", LogsResponse["Logfile"].AsString(), m_ServerLogTarget); } if (!m_CacheLogTarget.empty()) { - if (!CopyLog("cache", LogsResponse["cache"].AsObjectView()["Logfile"].AsString(), m_CacheLogTarget)) - { - return 1; - } + CopyLog("cache", LogsResponse["cache"].AsObjectView()["Logfile"].AsString(), m_CacheLogTarget); } if (!m_HttpLogTarget.empty()) { - if (!CopyLog("http", LogsResponse["http"].AsObjectView()["Logfile"].AsString(), m_HttpLogTarget)) - { - return 1; - } + CopyLog("http", LogsResponse["http"].AsObjectView()["Logfile"].AsString(), m_HttpLogTarget); } } else { Result.ThrowError("failed fetching log info"sv); - return 1; } - return 0; + return; } if (HttpClient::Response Result = Http.Post("/admin/logs", HttpClient::KeyValueMap{}, Parameters)) { @@ -599,10 +551,7 @@ LoggingCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) else { Result.ThrowError("failed setting log info"sv); - return 1; } - - return 0; } ////////////////////////////////////////////////////////////////////////// @@ -615,21 +564,21 @@ FlushCommand::FlushCommand() FlushCommand::~FlushCommand() = default; -int +void FlushCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } zen::HttpClient Http(m_HostName); @@ -638,18 +587,12 @@ FlushCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_CONSOLE("OK: {}", Response.ToText()); - return 0; - } - else if (int StatusCode = (int)Response.StatusCode) - { - ZEN_CONSOLE_ERROR("Flush failed: {}: {} ({})", StatusCode, ReasonStringForHttpResultCode(StatusCode), Response.ToText()); + return; } else { - ZEN_CONSOLE_ERROR("Flush failed: {}", Response.ToText()); + Response.ThrowError("Flush failed"); } - - return 1; } ////////////////////////////////////////////////////////////////////////// @@ -693,29 +636,29 @@ TryCopy(const std::filesystem::path& Source, const std::filesystem::path& Target return CopyFile(Source, Target, Options); } -int +void CopyStateCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } if (m_DataPath.empty()) { - throw OptionParseException("data path must be given"); + throw OptionParseException("'--data-path' is required", m_Options.help()); } if (!IsDir(m_DataPath)) { - throw OptionParseException("data path must exist"); + throw std::runtime_error(fmt::format("'--data-path' '{}' must exist", m_DataPath)); } if (m_TargetPath.empty()) { - throw OptionParseException("target path must be given"); + throw OptionParseException("'--target-path' is required", m_Options.help()); } std::filesystem::path RootManifestPath = m_DataPath / "root_manifest"; @@ -723,7 +666,8 @@ CopyStateCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (!TryCopy(RootManifestPath, TargetRootManifestPath)) { - throw OptionParseException("data path is invalid, missing root_manifest"); + throw std::runtime_error( + fmt::format("'--data-path' ('{}') is invalid, missing root_manifest at '{}'", m_DataPath, RootManifestPath)); } std::filesystem::path CachePath = m_DataPath / "cache"; @@ -828,8 +772,6 @@ CopyStateCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } } } - - return 0; } } // namespace zen diff --git a/src/zen/cmds/admin_cmd.h b/src/zen/cmds/admin_cmd.h index c593b2cac..4f97b7ad4 100644 --- a/src/zen/cmds/admin_cmd.h +++ b/src/zen/cmds/admin_cmd.h @@ -16,7 +16,7 @@ public: ScrubCommand(); ~ScrubCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -35,7 +35,7 @@ public: GcCommand(); ~GcCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -64,7 +64,7 @@ public: GcStatusCommand(); ~GcStatusCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -79,7 +79,7 @@ public: GcStopCommand(); ~GcStopCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -95,7 +95,7 @@ public: JobCommand(); ~JobCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -113,7 +113,7 @@ public: LoggingCommand(); ~LoggingCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -135,7 +135,7 @@ public: FlushCommand(); ~FlushCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -151,7 +151,7 @@ public: CopyStateCommand(); ~CopyStateCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: diff --git a/src/zen/cmds/bench_cmd.cpp b/src/zen/cmds/bench_cmd.cpp index d904a51f6..b9c45a328 100644 --- a/src/zen/cmds/bench_cmd.cpp +++ b/src/zen/cmds/bench_cmd.cpp @@ -25,14 +25,14 @@ BenchCommand::BenchCommand() BenchCommand::~BenchCommand() = default; -int +void BenchCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } #if ZEN_PLATFORM_WINDOWS @@ -97,7 +97,7 @@ BenchCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } #endif - return 0; + return; } } // namespace zen diff --git a/src/zen/cmds/bench_cmd.h b/src/zen/cmds/bench_cmd.h index 29d7fcc08..ed123be75 100644 --- a/src/zen/cmds/bench_cmd.h +++ b/src/zen/cmds/bench_cmd.h @@ -12,7 +12,7 @@ public: BenchCommand(); ~BenchCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } virtual ZenCmdCategory& CommandCategory() const override { return g_UtilitiesCategory; } diff --git a/src/zen/cmds/builds_cmd.cpp b/src/zen/cmds/builds_cmd.cpp index 2c6b8e50e..d858fa328 100644 --- a/src/zen/cmds/builds_cmd.cpp +++ b/src/zen/cmds/builds_cmd.cpp @@ -10472,7 +10472,7 @@ BuildsCommand::BuildsCommand() BuildsCommand::~BuildsCommand() = default; -int +void BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); @@ -10489,17 +10489,17 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) int ParentCommandArgCount = GetSubCommand(m_Options, argc, argv, m_SubCommands, SubOption, SubCommandArguments); if (!ParseOptions(ParentCommandArgCount, argv)) { - return 0; + return; } if (SubOption == nullptr) { - throw zen::OptionParseException("command verb is missing"); + throw OptionParseException("'verb' option is required", m_Options.help()); } if (!ParseOptions(*SubOption, gsl::narrow<int>(SubCommandArguments.size()), SubCommandArguments.data())) { - return 0; + return; } std::filesystem::path SystemRootDir; @@ -10522,19 +10522,22 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { if (!m_Host.empty()) { - throw zen::OptionParseException(fmt::format("host is not compatible with the url option\n{}", SubOption->help())); + throw OptionParseException(fmt::format("'--host' ('{}') conflicts with '--url' ('{}')", m_Host, m_Url), SubOption->help()); } if (!m_Bucket.empty()) { - throw zen::OptionParseException(fmt::format("bucket is not compatible with the url option\n{}", SubOption->help())); + throw OptionParseException(fmt::format("'--bucket' ('{}') conflicts with '--url' ('{}')", m_Bucket, m_Url), + SubOption->help()); } if (!m_BuildId.empty()) { - throw zen::OptionParseException(fmt::format("buildid is not compatible with the url option\n{}", SubOption->help())); + throw OptionParseException(fmt::format("'--buildid' ('{}') conflicts with '--url' ('{}')", m_BuildId, m_Url), + SubOption->help()); } if (!ParseCloudUrl(m_Url, m_Host, m_Namespace, m_Bucket, m_BuildId)) { - throw zen::OptionParseException(fmt::format("url does not match the Cloud Artifact URL format\n{}", SubOption->help())); + throw OptionParseException("'--url' ('{}') is malformed, it does not match the Cloud Artifact URL format", + SubOption->help()); } } @@ -10542,21 +10545,22 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { if (!m_StoragePath.empty()) { - throw zen::OptionParseException( - fmt::format("host/url/override-host is not compatible with the storage-path option\n{}", SubOption->help())); + throw OptionParseException( + fmt::format("'--storage-path' ('{}') conflicts with '--host'/'--url'/'--override-host' options", m_StoragePath), + SubOption->help()); } if (RequireNamespace && m_Namespace.empty()) { - throw zen::OptionParseException(fmt::format("namespace option is required for this storage option\n{}", SubOption->help())); + throw OptionParseException("'--namespace' is required", SubOption->help()); } if (RequireBucket && m_Bucket.empty()) { - throw zen::OptionParseException(fmt::format("bucket option is required for this storage option\n{}", SubOption->help())); + throw OptionParseException("'--bucket' is required", SubOption->help()); } } else if (m_StoragePath.empty()) { - throw zen::OptionParseException(fmt::format("At least one storage option is required\n{}", SubOption->help())); + throw OptionParseException("'--host', '--url', '--override-host' or '--storage-path' is required", SubOption->help()); } MakeSafeAbsolutePathÍnPlace(m_StoragePath); }; @@ -10594,11 +10598,11 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) .EncryptionIV = AesIV128Bit::FromString(m_EncryptionIV)}; if (!AuthMgrConfig.EncryptionKey.IsValid()) { - throw zen::OptionParseException("Invalid AES encryption key"); + throw OptionParseException(fmt::format("'--encryption-aes-key' ('{}') is malformed", m_EncryptionKey), SubOption->help()); } if (!AuthMgrConfig.EncryptionIV.IsValid()) { - throw zen::OptionParseException("Invalid AES initialization vector"); + throw OptionParseException(fmt::format("'--encryption-aes-iv' ('{}') is malformed", m_EncryptionIV), SubOption->help()); } Auth = AuthMgr::Create(AuthMgrConfig); } @@ -10701,7 +10705,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) auto ParseOutputOptions = [&]() { if (m_Verbose && m_Quiet) { - throw std::runtime_error("--verbose option is not compatible with --quiet option"); + throw OptionParseException("'--verbose' conflicts with '--quiet'", SubOption->help()); } IsVerbose = m_Verbose; IsQuiet = m_Quiet; @@ -10709,7 +10713,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { if (IsQuiet) { - throw std::runtime_error("--quiet option is not compatible with --log-progress option"); + throw OptionParseException("'--quiet' conflicts with '--log-progress'", SubOption->help()); } ProgressMode = ProgressBar::Mode::Log; } @@ -10717,7 +10721,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { if (IsQuiet) { - throw std::runtime_error("--quiet option is not compatible with --plain-progress option"); + throw OptionParseException("'--quiet' conflicts with '--plain-progress'", SubOption->help()); } ProgressMode = ProgressBar::Mode::Plain; } @@ -10952,7 +10956,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } else { - throw zen::OptionParseException(fmt::format("Storage option is missing\n{}", SubOption->help())); + throw OptionParseException("'--host', '--url', '--override-host' or '--storage-path' is required", SubOption->help()); } if (!m_ZenCacheHost.empty()) { @@ -10998,7 +11002,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) auto ParsePath = [&]() { if (m_Path.empty()) { - throw zen::OptionParseException(fmt::format("local-path is required\n{}", SubOption->help())); + throw OptionParseException("'--local-path' is required", SubOption->help()); } MakeSafeAbsolutePathÍnPlace(m_Path); }; @@ -11024,7 +11028,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) auto ParseDiffPath = [&]() { if (m_DiffPath.empty()) { - throw zen::OptionParseException(fmt::format("compare-path is required\n{}", SubOption->help())); + throw OptionParseException("'--compare-path' is required", SubOption->help()); } MakeSafeAbsolutePathÍnPlace(m_DiffPath); }; @@ -11033,13 +11037,20 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) m_BlobHash = RemoveQuotes(m_BlobHash); if (m_BlobHash.empty()) { - throw zen::OptionParseException(fmt::format("Blob hash string is missing\n{}", SubOption->help())); + throw OptionParseException("'--blob-hash' is required", SubOption->help()); + } + + if (m_BlobHash.length() != IoHash::StringLength) + { + throw OptionParseException( + fmt::format("'--blob-hash' ('{}') is malfomed, it must be {} characters long", m_BlobHash, IoHash::StringLength), + SubOption->help()); } IoHash BlobHash; if (!IoHash::TryParse(m_BlobHash, BlobHash)) { - throw zen::OptionParseException(fmt::format("Blob hash string is invalid\n{}", SubOption->help())); + throw OptionParseException(fmt::format("'--blob-hash' ('{}') is malformed", m_BlobHash), SubOption->help()); } return BlobHash; @@ -11049,11 +11060,13 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) m_BuildId = RemoveQuotes(m_BuildId); if (m_BuildId.length() != Oid::StringLength) { - throw zen::OptionParseException(fmt::format("Invalid build id\n{}", SubOption->help())); + throw OptionParseException( + fmt::format("'--build-id' ('{}') is malfomed, it must be {} characters long", m_BuildId, Oid::StringLength), + SubOption->help()); } else if (Oid BuildId = Oid::FromHexString(m_BuildId); BuildId == Oid::Zero) { - throw zen::OptionParseException(fmt::format("Invalid build id\n{}", SubOption->help())); + throw OptionParseException(fmt::format("'--build-id' ('{}') is invalid", m_BuildId), SubOption->help()); } else { @@ -11065,11 +11078,13 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) m_BuildPartId = RemoveQuotes(m_BuildPartId); if (m_BuildPartId.length() != Oid::StringLength) { - throw zen::OptionParseException(fmt::format("Invalid build part id\n{}", SubOption->help())); + throw OptionParseException( + fmt::format("'--build-id' ('{}') is malformed, it must be {} characters long", m_BuildPartId, Oid::StringLength), + SubOption->help()); } else if (Oid BuildPartId = Oid::FromHexString(m_BuildPartId); BuildPartId == Oid::Zero) { - throw zen::OptionParseException(fmt::format("Invalid build part id\n{}", SubOption->help())); + throw OptionParseException(fmt::format("'--build-id' ('{}') is malformed", m_BuildPartId), SubOption->help()); } else { @@ -11084,7 +11099,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) BuildPartIds.push_back(Oid::TryFromHexString(RemoveQuotes(BuildPartId))); if (BuildPartIds.back() == Oid::Zero) { - throw zen::OptionParseException(fmt::format("build-part-id '{}' is invalid\n{}", BuildPartId, SubOption->help())); + throw OptionParseException(fmt::format("'--build-part-id' ('{}') is malformed", BuildPartId), SubOption->help()); } } return BuildPartIds; @@ -11097,7 +11112,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) BuildPartNames.push_back(std::string(RemoveQuotes(BuildPartName))); if (BuildPartNames.back().empty()) { - throw zen::OptionParseException(fmt::format("build-part-names '{}' is invalid\n{}", BuildPartName, SubOption->help())); + throw OptionParseException(fmt::format("'--build-part-names' ('{}') is invalid", BuildPartName), SubOption->help()); } } return BuildPartNames; @@ -11108,11 +11123,13 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { if (m_BuildMetadataPath.empty() && m_BuildMetadata.empty()) { - throw zen::OptionParseException(fmt::format("Options for builds target are missing\n{}", SubOption->help())); + throw OptionParseException("'--metadata-path' or '--metadata' is required", SubOption->help()); } if (!m_BuildMetadataPath.empty() && !m_BuildMetadata.empty()) { - throw zen::OptionParseException(fmt::format("Conflicting options for builds target\n{}", SubOption->help())); + throw OptionParseException( + fmt::format("'--metadata-path' ('{}') conflicts with '--metadata' ('{}')", m_BuildMetadataPath, m_BuildMetadata), + SubOption->help()); } if (!m_BuildMetadataPath.empty()) @@ -11149,12 +11166,11 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { if (!m_BuildMetadataPath.empty()) { - throw zen::OptionParseException( - fmt::format("metadata-path option is only valid if creating a build\n{}", SubOption->help())); + throw OptionParseException("'--metadata-path' requires '--create-build'", SubOption->help()); } if (!m_BuildMetadata.empty()) { - throw zen::OptionParseException(fmt::format("metadata option is only valid if creating a build\n{}", SubOption->help())); + throw OptionParseException("'--metadata' requires '--create-build'", SubOption->help()); } } return {}; @@ -11220,8 +11236,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) WriteFile(ListResultPath, IoBuffer(IoBuffer::Wrap, SB.Data(), SB.Size())); } } - - return 0; + return; } if (SubOption == &m_ListOptions) @@ -11313,8 +11328,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) WriteFile(m_ListResultPath, IoBuffer(IoBuffer::Wrap, SB.Data(), SB.Size())); } } - - return 0; + return; } if (SubOption == &m_UploadOptions) @@ -11412,7 +11426,10 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) : 0); } } - return AbortFlag ? 11 : 0; + if (AbortFlag) + { + throw std::runtime_error("Upload aborted"); + } } if (SubOption == &m_DownloadOptions) @@ -11446,8 +11463,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (m_PostDownloadVerify && m_PrimeCacheOnly) { - throw zen::OptionParseException( - fmt::format("'cache-prime-only' option is not compatible with 'verify' option\n{}", SubOption->help())); + throw OptionParseException("'--cache-prime-only' conflicts with '--verify'", SubOption->help()); } if (m_Clean && m_PrimeCacheOnly) @@ -11480,7 +11496,10 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) m_IncludeWildcard, m_ExcludeWildcard); - return AbortFlag ? 11 : 0; + if (AbortFlag) + { + throw std::runtime_error("Download aborted"); + } } if (SubOption == &m_LsOptions) @@ -11516,7 +11535,10 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ListBuild(Storage, BuildId, BuildPartIds, BuildPartNames, m_IncludeWildcard, m_ExcludeWildcard); - return AbortFlag ? 11 : 0; + if (AbortFlag) + { + throw std::runtime_error("List build aborted"); + } } if (SubOption == &m_DiffOptions) @@ -11525,7 +11547,10 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ParseDiffPath(); DiffFolders(m_Path, m_DiffPath, m_OnlyChunked); - return AbortFlag ? 11 : 0; + if (AbortFlag) + { + throw std::runtime_error("Diff folders aborted"); + } } if (SubOption == &m_FetchBlobOptions) @@ -11568,7 +11593,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ValidateBlob(*Storage.BuildStorage, BuildId, BlobHash, CompressedSize, DecompressedSize); if (AbortFlag) { - return 11; + throw std::runtime_error("Fetch blob aborted"); } if (!IsQuiet) { @@ -11577,7 +11602,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) CompressedSize, DecompressedSize); } - return 0; + return; } if (SubOption == &m_ValidateBuildPartOptions) @@ -11617,7 +11642,9 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (!m_BuildPartName.empty() && !m_BuildPartId.empty()) { - throw zen::OptionParseException(fmt::format("build-part-id conflicts with build-part-name\n{}", SubOption->help())); + throw OptionParseException( + fmt::format("'--build-part-id' ('{}') conflicts with '--build-part-name' ('{}')", m_BuildPartId, m_BuildPartName), + SubOption->help()); } const Oid BuildPartId = m_BuildPartName.empty() ? Oid::Zero : ParseBuildPartId(); @@ -11626,7 +11653,10 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) DownloadStatistics DownloadStats; ValidateBuildPart(*Storage.BuildStorage, BuildId, BuildPartId, m_BuildPartName, ValidateStats, DownloadStats); - return AbortFlag ? 13 : 0; + if (AbortFlag) + { + throw std::runtime_error("Validate build part failed"); + } } if (SubOption == &m_MultiTestDownloadOptions) @@ -11659,7 +11689,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) Oid BuildId = Oid::FromHexString(RemoveQuotes(BuildIdString)); if (BuildId == Oid::Zero) { - throw zen::OptionParseException(fmt::format("invalid build id {}\n{}", BuildIdString, SubOption->help())); + throw OptionParseException(fmt::format("'--build-id' ('{}') is malformed", BuildIdString), SubOption->help()); } DownloadFolder(Storage, BuildId, @@ -11678,8 +11708,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ""sv); if (AbortFlag) { - ZEN_CONSOLE("Download cancelled"); - return 11; + throw std::runtime_error("Multitest aborted"); } if (!IsQuiet) { @@ -11690,7 +11719,6 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_CONSOLE("Completed in {}", NiceTimeSpanMs(Timer.GetElapsedTimeMs())); } - return 0; } auto ParseZenProcessId = [&]() { @@ -11706,7 +11734,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } if (!RunningProcess.IsValid()) { - throw zen::OptionParseException( + throw std::runtime_error( fmt::format("Unable to find a running instance of the zen executable '{}'", RunningExecutablePath)); } m_ZenProcessId = RunningProcess.Pid(); @@ -11718,7 +11746,6 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ParseZenProcessId(); ZenState RunningState(m_ZenProcessId); RunningState.StateData().Pause.store(true); - return 0; } if (SubOption == &m_ResumeOptions) @@ -11726,7 +11753,6 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ParseZenProcessId(); ZenState RunningState(m_ZenProcessId); RunningState.StateData().Pause.store(false); - return 0; } if (SubOption == &m_AbortOptions) @@ -11734,7 +11760,6 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ParseZenProcessId(); ZenState RunningState(m_ZenProcessId); RunningState.StateData().Abort.store(true); - return 0; } if (SubOption == &m_TestOptions) @@ -11834,8 +11859,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) true); if (AbortFlag) { - ZEN_CONSOLE_ERROR("Upload failed."); - return 11; + throw std::runtime_error("Test aborted. (Upload build)"); } ZEN_CONSOLE("\nDownload Build {}, Part {} ({}) to '{}'", BuildId, BuildPartId, m_BuildPartName, DownloadPath); @@ -11856,8 +11880,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ""sv); if (AbortFlag) { - ZEN_CONSOLE_ERROR("Download failed."); - return 11; + throw std::runtime_error("Test aborted. (Download build)"); } ZEN_CONSOLE("\nRe-download Build {}, Part {} ({}) to '{}' (identical target)", BuildId, BuildPartId, m_BuildPartName, DownloadPath); @@ -11878,8 +11901,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ""sv); if (AbortFlag) { - ZEN_CONSOLE_ERROR("Re-download failed. (identical target)"); - return 11; + throw std::runtime_error("Test aborted. (Re-download identical target)"); } auto ScrambleDir = [](const std::filesystem::path& Path) { @@ -11992,8 +12014,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ""sv); if (AbortFlag) { - ZEN_CONSOLE_ERROR("Re-download failed. (scrambled target)"); - return 11; + throw std::runtime_error("Test aborted. (Re-download scrambled target)"); } ScrambleDir(DownloadPath); @@ -12024,8 +12045,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) true); if (AbortFlag) { - ZEN_CONSOLE_ERROR("Upload of scrambled failed."); - return 11; + throw std::runtime_error("Test aborted. (Upload scrambled)"); } ZEN_CONSOLE("\nDownload Build {}, Part {} ({}) to '{}' (original)", BuildId, BuildPartId, m_BuildPartName, DownloadPath); @@ -12046,8 +12066,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ""sv); if (AbortFlag) { - ZEN_CONSOLE_ERROR("Re-download failed."); - return 11; + throw std::runtime_error("Test aborted. (Download original)"); } ZEN_CONSOLE("\nDownload Build {}, Part {} ({}) to '{}' (scrambled)", BuildId2, BuildPartId2, m_BuildPartName, DownloadPath); @@ -12068,8 +12087,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ""sv); if (AbortFlag) { - ZEN_CONSOLE_ERROR("Re-download failed."); - return 11; + throw std::runtime_error("Test aborted. (Download scrambled)"); } ZEN_CONSOLE("\nRe-download Build {}, Part {} ({}) to '{}' (scrambled)", BuildId2, BuildPartId2, m_BuildPartName, DownloadPath); @@ -12090,8 +12108,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ""sv); if (AbortFlag) { - ZEN_CONSOLE_ERROR("Re-download failed."); - return 11; + throw std::runtime_error("Test aborted. (Re-download scrambled)"); } ZEN_CONSOLE("\nDownload Build {}, Part {} ({}) to '{}' (original)", BuildId, BuildPartId, m_BuildPartName, DownloadPath2); @@ -12112,13 +12129,9 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ""sv); if (AbortFlag) { - ZEN_CONSOLE_ERROR("Re-download failed."); - return 11; + throw std::runtime_error("Test aborted. (Download original)"); } - - return 0; } - ZEN_ASSERT(false); } } // namespace zen diff --git a/src/zen/cmds/builds_cmd.h b/src/zen/cmds/builds_cmd.h index d057d24ac..79d69b16c 100644 --- a/src/zen/cmds/builds_cmd.h +++ b/src/zen/cmds/builds_cmd.h @@ -13,13 +13,14 @@ namespace zen { class BuildsCommand : public CacheStoreCommand { public: - static constexpr char Name[] = "builds"; - static constexpr char Description[] = "Manage builds - list, upload, download, diff"; + static constexpr char Name[] = "builds"; + static constexpr char Description[] = + "Manage builds - list, list-namespaces, ls, upload, download, diff, fetch-blob, validate-part, pause, resume, abort"; BuildsCommand(); ~BuildsCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: diff --git a/src/zen/cmds/cache_cmd.cpp b/src/zen/cmds/cache_cmd.cpp index 90f428b29..ea7ad79ee 100644 --- a/src/zen/cmds/cache_cmd.cpp +++ b/src/zen/cmds/cache_cmd.cpp @@ -70,59 +70,53 @@ DropCommand::DropCommand() DropCommand::~DropCommand() = default; -int +void DropCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw zen::OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } if (m_NamespaceName.empty()) { - throw zen::OptionParseException("Drop command requires a namespace"); + throw OptionParseException("'--namespace' is required", m_Options.help()); } - cpr::Session Session; + std::string Url; + std::string DropDescription; + if (m_BucketName.empty()) { - ZEN_CONSOLE("Dropping cache namespace '{}' from '{}'", m_NamespaceName, m_HostName); - Session.SetUrl({fmt::format("{}/z$/{}", m_HostName, m_NamespaceName)}); + DropDescription = fmt::format("cache namespace '{}' from '{}'", m_NamespaceName, m_HostName); + Url = fmt::format("/z$/{}", m_NamespaceName); } else { - ZEN_CONSOLE("Dropping cache bucket '{}/{}' from '{}'", m_NamespaceName, m_BucketName, m_HostName); - Session.SetUrl({fmt::format("{}/z$/{}/{}", m_HostName, m_NamespaceName, m_BucketName)}); + DropDescription = fmt::format("cache bucket '{}/{}' from '{}'", m_NamespaceName, m_BucketName, m_HostName); + Url = fmt::format("/z$/{}/{}", m_NamespaceName, m_BucketName); } - cpr::Response Result = Session.Delete(); - - if (zen::IsHttpSuccessCode(Result.status_code)) - { - ZEN_CONSOLE("OK: drop succeeded"); - - return 0; - } + ZEN_CONSOLE("Dropping {}", DropDescription); - if (Result.status_code) + HttpClient Http(m_HostName); + if (HttpClient::Response Response = Http.Delete(Url)) { - ZEN_CONSOLE_ERROR("Drop failed: {}: {} ({})", Result.status_code, Result.reason, Result.text); + ZEN_CONSOLE("{}", Response.ToText()); } else { - ZEN_CONSOLE_ERROR("Drop failed: {}", Result.error.message); + Response.ThrowError(fmt::format("Failed to drop {}", DropDescription)); } - - return 1; } CacheInfoCommand::CacheInfoCommand() @@ -144,87 +138,76 @@ CacheInfoCommand::CacheInfoCommand() CacheInfoCommand::~CacheInfoCommand() = default; -int +void CacheInfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw zen::OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } - cpr::Session Session; - Session.SetHeader(cpr::Header{{"Accept", "application/json"}}); + std::string Url; if (m_HostName.empty()) { if (!m_SizeInfoBucketNames.empty()) { - throw zen::OptionParseException("--bucketsizes option needs a --namespace"); + throw OptionParseException("'--bucketsizes' requires '--namespace'", m_Options.help()); } if (m_BucketSizeInfo) { - throw zen::OptionParseException("--bucketsizes option needs a --namespace and a --bucket"); + throw OptionParseException("'--bucketsize' requires '--namespace' and '--bucket'", m_Options.help()); } ZEN_CONSOLE("Info on cache from '{}'", m_HostName); - Session.SetUrl({fmt::format("{}/z$", m_HostName)}); + Url = "/z$"; } else if (m_BucketName.empty()) { if (m_BucketSizeInfo) { - throw zen::OptionParseException("--bucketsizes option needs a --bucket"); + throw OptionParseException(fmt::format("'--bucketsize' requires '--namespace' and '--bucket' ('{}')", m_BucketName), + m_Options.help()); } ZEN_CONSOLE("Info on cache namespace '{}' from '{}'", m_NamespaceName, m_HostName); - Session.SetUrl({fmt::format("{}/z$/{}", m_HostName, m_NamespaceName)}); + Url = fmt::format("/z$/{}", m_NamespaceName); } else { if (!m_SizeInfoBucketNames.empty()) { - throw zen::OptionParseException("--bucketsizes option can not be used together with --bucket option"); + throw OptionParseException("'--bucketsizes' conflicts with '--bucket'", m_Options.help()); } ZEN_CONSOLE("Info on cache bucket '{}/{}' from '{}'", m_NamespaceName, m_BucketName, m_HostName); - Session.SetUrl({fmt::format("{}/z$/{}/{}", m_HostName, m_NamespaceName, m_BucketName)}); + Url = fmt::format("/z$/{}/{}", m_NamespaceName, m_BucketName); } - cpr::Parameters Parameters; + HttpClient::KeyValueMap Parameters; if (!m_SizeInfoBucketNames.empty()) { - Parameters.Add({"bucketsizes", m_SizeInfoBucketNames}); + Parameters.Entries.insert({"bucketsizes", m_SizeInfoBucketNames}); } if (m_BucketSizeInfo) { - Parameters.Add({"bucketsize", "true"}); - } - - Session.SetParameters(Parameters); - cpr::Response Result = Session.Get(); - - if (zen::IsHttpSuccessCode(Result.status_code)) - { - ZEN_CONSOLE("{}", Result.text); - - return 0; + Parameters.Entries.insert({"bucketsize", "true"}); } - if (Result.status_code) + HttpClient Http(m_HostName); + if (HttpClient::Response Response = Http.Get(Url, HttpClient::Accept(ZenContentType::kJSON), Parameters)) { - ZEN_CONSOLE_ERROR("Info failed: {}: {} ({})", Result.status_code, Result.reason, Result.text); + ZEN_CONSOLE("{}", Response.ToText()); } else { - ZEN_CONSOLE_ERROR("Info failed: {}", Result.error.message); + Response.ThrowError("Info failed"); } - - return 1; } CacheStatsCommand::CacheStatsCommand() @@ -235,46 +218,32 @@ CacheStatsCommand::CacheStatsCommand() CacheStatsCommand::~CacheStatsCommand() = default; -int +void CacheStatsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw zen::OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } - cpr::Session Session; - Session.SetUrl({fmt::format("{}/stats/z$", m_HostName)}); - Session.SetHeader(cpr::Header{{"Accept", "application/json"}}); - - cpr::Response Result = Session.Get(); - - if (zen::IsHttpSuccessCode(Result.status_code)) - { - ZEN_CONSOLE("{}", Result.text); - - return 0; - } - - if (Result.status_code) + HttpClient Http(m_HostName); + if (HttpClient::Response Response = Http.Get("/stats/z$", HttpClient::Accept(ZenContentType::kJSON))) { - ZEN_CONSOLE_ERROR("Info failed: {}: {} ({})", Result.status_code, Result.reason, Result.text); + ZEN_CONSOLE("{}", Response.ToText()); } else { - ZEN_CONSOLE_ERROR("Info failed: {}", Result.error.message); + Response.ThrowError("Info failed"); } - - return 1; } CacheDetailsCommand::CacheDetailsCommand() @@ -296,87 +265,82 @@ CacheDetailsCommand::CacheDetailsCommand() CacheDetailsCommand::~CacheDetailsCommand() = default; -int +void CacheDetailsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw zen::OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } - cpr::Session Session; - cpr::Parameters Parameters; + HttpClient::KeyValueMap Parameters; if (m_Details) { - Parameters.Add({"details", "true"}); + Parameters.Entries.insert({"details", "true"}); } if (m_AttachmentDetails) { - Parameters.Add({"attachmentdetails", "true"}); + Parameters.Entries.insert({"attachmentdetails", "true"}); } + + HttpClient::KeyValueMap Headers; if (m_CSV) { - Parameters.Add({"csv", "true"}); + Parameters.Entries.insert({"csv", "true"}); } else { - Session.SetHeader(cpr::Header{{"Accept", "application/json"}}); + Headers = HttpClient::Accept(ZenContentType::kJSON); } + std::string Url; if (!m_ValueKey.empty()) { - if (m_Namespace.empty() || m_Bucket.empty()) + if (m_Namespace.empty()) + { + throw OptionParseException("'--namespace' is required", m_Options.help()); + } + if (m_Bucket.empty()) { - throw OptionParseException("Provide namespace and bucket name"); + throw OptionParseException("'--bucket' is required", m_Options.help()); } - Session.SetUrl({fmt::format("{}/z$/details$/{}/{}/{}", m_HostName, m_Namespace, m_Bucket, m_ValueKey)}); + Url = fmt::format("/z$/details$/{}/{}/{}", m_Namespace, m_Bucket, m_ValueKey); } else if (!m_Bucket.empty()) { if (m_Namespace.empty()) { - throw OptionParseException("Provide namespace name"); + throw OptionParseException("'--namespace' is required", m_Options.help()); } - Session.SetUrl({fmt::format("{}/z$/details$/{}/{}", m_HostName, m_Namespace, m_Bucket)}); + Url = fmt::format("/z$/details$/{}/{}", m_Namespace, m_Bucket); } else if (!m_Namespace.empty()) { - Session.SetUrl({fmt::format("{}/z$/details$/{}", m_HostName, m_Namespace)}); + Url = fmt::format("/z$/details$/{}", m_Namespace); } else { - Session.SetUrl({fmt::format("{}/z$/details$", m_HostName)}); + Url = "/z$/details$"; } - Session.SetParameters(Parameters); - - cpr::Response Result = Session.Get(); - if (zen::IsHttpSuccessCode(Result.status_code)) - { - ZEN_CONSOLE("{}", Result.text); - - return 0; - } - - if (Result.status_code) + HttpClient Http(m_HostName); + if (HttpClient::Response Response = Http.Get(Url, Headers, Parameters)) { - ZEN_CONSOLE_ERROR("Info failed: {}: {} ({})", Result.status_code, Result.reason, Result.text); + ZEN_CONSOLE("{}", Response.ToText()); } else { - ZEN_CONSOLE_ERROR("Info failed: {}", Result.error.message); + Response.ThrowError("Info failed"); } - - return 1; } CacheGenerateCommand::CacheGenerateCommand() @@ -407,21 +371,21 @@ CacheGenerateCommand::CacheGenerateCommand() CacheGenerateCommand::~CacheGenerateCommand() = default; -int +void CacheGenerateCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw zen::OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } if (m_MaxSize == 0 && m_MinSize == 0) @@ -542,8 +506,6 @@ CacheGenerateCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a { ZEN_INFO("Creating data, {} requests remaining", WorkLatch.Remaining()); } - - return 0; } CacheGetCommand::CacheGetCommand() @@ -570,7 +532,7 @@ CacheGetCommand::CacheGetCommand() CacheGetCommand::~CacheGetCommand() = default; -int +void CacheGetCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); @@ -579,35 +541,35 @@ CacheGetCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw zen::OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } if (m_Namespace.empty()) { - throw zen::OptionParseException("cache-get command requires a namespace"); + throw OptionParseException("'--namespace' is required", m_Options.help()); } if (m_Bucket.empty()) { - throw zen::OptionParseException("cache-get command requires a bucket"); + throw OptionParseException("'--bucket' is required", m_Options.help()); } if (m_ValueKey.empty()) { - throw zen::OptionParseException("cache-get command requires a value key"); + throw OptionParseException("'--valuekey' is required", m_Options.help()); } IoHash ValueId; if (!IoHash::TryParse(m_ValueKey, ValueId)) { - throw zen::OptionParseException("cache-get --valuekey option requires a valid IoHash string"); + throw OptionParseException(fmt::format("'--value-key' ('{}') is malformed", m_ValueKey), m_Options.help()); } IoHash AttachmentHash; @@ -615,7 +577,7 @@ CacheGetCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { if (!IoHash::TryParse(m_AttachmentHash, AttachmentHash)) { - throw zen::OptionParseException("cache-get --attachmenthash option requires a valid IoHash string"); + throw OptionParseException(fmt::format("'--attachmenthash' ('{}') is malformed", m_AttachmentHash), m_Options.help()); } } @@ -681,8 +643,6 @@ CacheGetCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { Result.ThrowError("Failed to fetch data"sv); } - - return 0; } } // namespace zen diff --git a/src/zen/cmds/cache_cmd.h b/src/zen/cmds/cache_cmd.h index b8a319359..4dc05bbdc 100644 --- a/src/zen/cmds/cache_cmd.h +++ b/src/zen/cmds/cache_cmd.h @@ -12,7 +12,7 @@ public: DropCommand(); ~DropCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -27,7 +27,7 @@ class CacheInfoCommand : public CacheStoreCommand public: CacheInfoCommand(); ~CacheInfoCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -44,7 +44,7 @@ class CacheStatsCommand : public CacheStoreCommand public: CacheStatsCommand(); ~CacheStatsCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -57,7 +57,7 @@ class CacheDetailsCommand : public CacheStoreCommand public: CacheDetailsCommand(); ~CacheDetailsCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -79,7 +79,7 @@ public: CacheGenerateCommand(); ~CacheGenerateCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -104,7 +104,7 @@ public: CacheGetCommand(); ~CacheGetCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: diff --git a/src/zen/cmds/copy_cmd.cpp b/src/zen/cmds/copy_cmd.cpp index 4e54f27bb..530661607 100644 --- a/src/zen/cmds/copy_cmd.cpp +++ b/src/zen/cmds/copy_cmd.cpp @@ -24,23 +24,23 @@ CopyCommand::CopyCommand() CopyCommand::~CopyCommand() = default; -int +void CopyCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); - if (!ZenCmdBase::ParseOptions(argc, argv)) + if (!ParseOptions(argc, argv)) { - return 0; + return; } // Validate arguments if (m_CopySource.empty()) - throw std::runtime_error("No source specified"); + throw OptionParseException("'--source' is required", m_Options.help()); if (m_CopyTarget.empty()) - throw std::runtime_error("No target specified"); + throw OptionParseException("'--target' is required", m_Options.help()); std::filesystem::path FromPath = m_CopySource; std::filesystem::path ToPath = m_CopyTarget; @@ -180,9 +180,7 @@ CopyCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (Visitor.FailedFileCount) { - ZEN_CONSOLE_ERROR("{} file copy operations FAILED", Visitor.FailedFileCount); - - return 1; + throw std::runtime_error(fmt::format("{} file copy operations FAILED", Visitor.FailedFileCount)); } } else @@ -194,27 +192,16 @@ CopyCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) zen::CopyFileOptions CopyOptions; CopyOptions.EnableClone = !m_NoClone; - try + zen::CreateDirectories(ToPath.parent_path()); + if (zen::CopyFile(FromPath, ToPath, CopyOptions)) { - zen::CreateDirectories(ToPath.parent_path()); - if (zen::CopyFile(FromPath, ToPath, CopyOptions)) - { - ZEN_CONSOLE("Copy completed in {}", zen::NiceTimeSpanMs(Timer.GetElapsedTimeMs())); - } - else - { - throw std::logic_error("CopyFile failed in an unexpected way"); - } + ZEN_CONSOLE("Copy completed in {}", zen::NiceTimeSpanMs(Timer.GetElapsedTimeMs())); } - catch (const std::exception& Ex) + else { - ZEN_CONSOLE_ERROR("Failed to copy '{}' to '{}': '{}'", FromPath, ToPath, Ex.what()); - - return 1; + throw std::runtime_error(fmt::format("Failed to copy '{}' to '{}'", FromPath, ToPath)); } } - - return 0; } } // namespace zen diff --git a/src/zen/cmds/copy_cmd.h b/src/zen/cmds/copy_cmd.h index e9735c159..e1a5dcb82 100644 --- a/src/zen/cmds/copy_cmd.h +++ b/src/zen/cmds/copy_cmd.h @@ -15,7 +15,7 @@ public: ~CopyCommand(); virtual cxxopts::Options& Options() override { return m_Options; } - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual ZenCmdCategory& CommandCategory() const override { return g_UtilitiesCategory; } private: diff --git a/src/zen/cmds/dedup_cmd.cpp b/src/zen/cmds/dedup_cmd.cpp index 033ac87c3..9ef50a97d 100644 --- a/src/zen/cmds/dedup_cmd.cpp +++ b/src/zen/cmds/dedup_cmd.cpp @@ -51,14 +51,14 @@ DedupCommand::DedupCommand() DedupCommand::~DedupCommand() = default; -int +void DedupCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } // Validate arguments @@ -68,14 +68,12 @@ DedupCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (!SourceGood) { - ZEN_CONSOLE_ERROR("Source directory '{}' does not support deduplication", m_DedupSource); - return 0; + throw std::runtime_error(fmt::format("Source directory '{}' does not support deduplication", m_DedupSource)); } if (!TargetGood) { - ZEN_CONSOLE_ERROR("Target directory '{}' does not support deduplication", m_DedupTarget); - return 0; + throw std::runtime_error(fmt::format("Target directory '{}' does not support deduplication", m_DedupTarget)); } ZEN_CONSOLE("Performing dedup operation between {} and {}, size threshold {}", @@ -295,8 +293,6 @@ DedupCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } ZEN_CONSOLE("Elapsed: {} Deduped: {}", zen::NiceTimeSpanMs(Timer.GetElapsedTimeMs()), zen::NiceBytes(DupeBytes)); - - return 0; } } // namespace zen diff --git a/src/zen/cmds/dedup_cmd.h b/src/zen/cmds/dedup_cmd.h index 2721be2b9..5b8387dd2 100644 --- a/src/zen/cmds/dedup_cmd.h +++ b/src/zen/cmds/dedup_cmd.h @@ -15,7 +15,7 @@ public: ~DedupCommand(); virtual cxxopts::Options& Options() override { return m_Options; } - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual ZenCmdCategory& CommandCategory() const override { return g_UtilitiesCategory; } private: diff --git a/src/zen/cmds/info_cmd.cpp b/src/zen/cmds/info_cmd.cpp index 8e0b3d663..49ad022cf 100644 --- a/src/zen/cmds/info_cmd.cpp +++ b/src/zen/cmds/info_cmd.cpp @@ -21,21 +21,21 @@ InfoCommand::~InfoCommand() { } -int +void InfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } HttpClient Http(m_HostName); @@ -48,8 +48,6 @@ InfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { Result.ThrowError(fmt::format("Failed getting info from {}", m_HostName)); } - - return 0; } } // namespace zen diff --git a/src/zen/cmds/info_cmd.h b/src/zen/cmds/info_cmd.h index 9723a075b..231565bfd 100644 --- a/src/zen/cmds/info_cmd.h +++ b/src/zen/cmds/info_cmd.h @@ -12,7 +12,7 @@ public: InfoCommand(); ~InfoCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } // virtual ZenCmdCategory& CommandCategory() const override { return g_UtilitiesCategory; } diff --git a/src/zen/cmds/print_cmd.cpp b/src/zen/cmds/print_cmd.cpp index fd6d92f28..c3c11a0ea 100644 --- a/src/zen/cmds/print_cmd.cpp +++ b/src/zen/cmds/print_cmd.cpp @@ -55,20 +55,20 @@ PrintCommand::PrintCommand() PrintCommand::~PrintCommand() = default; -int +void PrintCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } // Validate arguments if (m_Filename.empty()) - throw std::runtime_error("No file specified"); + throw OptionParseException("'--source' is required", m_Options.help()); FileContents Fc; @@ -84,9 +84,7 @@ PrintCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (Fc.ErrorCode) { - ZEN_CONSOLE_ERROR("Failed to read file '{}': {}", m_Filename, Fc.ErrorCode.message()); - - return 1; + throw std::runtime_error(fmt::format("Failed to read file '{}': {}", m_Filename, Fc.ErrorCode.message())); } IoBuffer Data = Fc.Flatten(); @@ -158,11 +156,11 @@ PrintCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (FilteredResult != CbValidateError::None) { - ZEN_CONSOLE_ERROR("Object in package message file '{}' does not appear to be compact binary (validation error {:#x}: '{}')", - m_Filename, - uint32_t(FilteredResult), - ToString(FilteredResult)); - return 1; + throw std::runtime_error( + fmt::format("Object in package message file '{}' does not appear to be compact binary (validation error {:#x}: '{}')", + m_Filename, + uint32_t(FilteredResult), + ToString(FilteredResult))); } else { @@ -185,18 +183,16 @@ PrintCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (FilteredResult != CbValidateError::None) { - ZEN_CONSOLE_ERROR("Data in file '{}' does not appear to be compact binary (validation error {:#x}: '{}')", - m_Filename, - uint32_t(FilteredResult), - ToString(FilteredResult)); - return 1; + throw std::runtime_error(fmt::format("Data in file '{}' does not appear to be compact binary (validation error {:#x}: '{}')", + m_Filename, + uint32_t(FilteredResult), + ToString(FilteredResult))); } else { PrintCompactBinary(Data, m_ShowCbObjectTypeInfo); } } - return 0; } ////////////////////////////////////////////////////////////////////////// @@ -218,20 +214,20 @@ PrintPackageCommand::~PrintPackageCommand() { } -int +void PrintPackageCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } // Validate arguments if (m_Filename.empty()) - throw std::runtime_error("No file specified"); + throw OptionParseException("'--source' is required", m_Options.help()); MakeSafeAbsolutePathÍnPlace(m_Filename); FileContents Fc = ReadFile(m_Filename); @@ -250,8 +246,6 @@ PrintPackageCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** ar { ZEN_ERROR("error: malformed package?"); } - - return 0; } } // namespace zen diff --git a/src/zen/cmds/print_cmd.h b/src/zen/cmds/print_cmd.h index 80729901e..6c1529b7c 100644 --- a/src/zen/cmds/print_cmd.h +++ b/src/zen/cmds/print_cmd.h @@ -14,7 +14,7 @@ public: PrintCommand(); ~PrintCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } virtual ZenCmdCategory& CommandCategory() const override { return g_UtilitiesCategory; } @@ -32,7 +32,7 @@ public: PrintPackageCommand(); ~PrintPackageCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } virtual ZenCmdCategory& CommandCategory() const override { return g_UtilitiesCategory; } diff --git a/src/zen/cmds/projectstore_cmd.cpp b/src/zen/cmds/projectstore_cmd.cpp index 5f0f52f7c..11c915e9a 100644 --- a/src/zen/cmds/projectstore_cmd.cpp +++ b/src/zen/cmds/projectstore_cmd.cpp @@ -149,16 +149,6 @@ namespace { } } - class AsyncJobError : public std::runtime_error - { - public: - using _Mybase = runtime_error; - - AsyncJobError(const std::string& Message, int ReturnCode) : _Mybase(Message), m_ReturnCode(ReturnCode) {} - - const int m_ReturnCode = 0; - }; - void ExecuteAsyncOperation(HttpClient& Http, std::string_view Url, IoBuffer&& Payload, bool PlainProgress) { signal(SIGINT, SignalCallbackHandler); @@ -265,11 +255,11 @@ namespace { int ReturnCode = StatusObject["ReturnCode"].AsInt32(-1); if (!AbortReason.empty()) { - throw AsyncJobError(std::string(AbortReason), ReturnCode); + throw ErrorWithReturnCode(std::string(AbortReason), ReturnCode); } else { - throw AsyncJobError("Aborted", ReturnCode); + throw ErrorWithReturnCode("Aborted", ReturnCode); } break; } @@ -564,21 +554,21 @@ DropProjectCommand::~DropProjectCommand() { } -int +void DropProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } HttpClient Http(m_HostName); @@ -586,7 +576,7 @@ DropProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg m_ProjectName = ResolveProject(Http, m_ProjectName); if (m_ProjectName.empty()) { - return 1; + throw std::runtime_error(fmt::format("Can't find project '{}'", m_ProjectName)); } if (m_OplogName.empty()) @@ -605,7 +595,6 @@ DropProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg else { Result.ThrowError("delete project failed"sv); - return 1; } } } @@ -614,7 +603,7 @@ DropProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg m_OplogName = ResolveOplog(Http, m_ProjectName, m_OplogName); if (m_OplogName.empty()) { - return 1; + throw std::runtime_error(fmt::format("Can't find oplog in project '{}'", m_OplogName, m_ProjectName)); } if (m_DryRun) { @@ -633,12 +622,9 @@ DropProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg else { Result.ThrowError("delete oplog failed"sv); - return 1; } } } - - return 0; } /////////////////////////////////////// @@ -657,26 +643,26 @@ ProjectInfoCommand::~ProjectInfoCommand() { } -int +void ProjectInfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } if (!m_OplogName.empty() && m_ProjectName.empty()) { - throw OptionParseException("an oplog can't be specified without also specifying a project"); + throw OptionParseException("'--project' is required", m_Options.help()); } HttpClient Http(m_HostName); @@ -692,7 +678,7 @@ ProjectInfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg m_ProjectName = ResolveProject(Http, m_ProjectName); if (m_ProjectName.empty()) { - return 1; + throw std::runtime_error("Unable to resolve project"); } Url = fmt::format("/prj/{}", m_ProjectName); @@ -703,13 +689,13 @@ ProjectInfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg m_ProjectName = ResolveProject(Http, m_ProjectName); if (m_ProjectName.empty()) { - return 1; + throw std::runtime_error("Unable to resolve project"); } m_OplogName = ResolveOplog(Http, m_ProjectName, m_OplogName); if (m_OplogName.empty()) { - return 1; + throw std::runtime_error("Unable to resolve oplog"); } Url = fmt::format("/prj/{}/oplog/{}", m_ProjectName, m_OplogName); @@ -719,12 +705,10 @@ ProjectInfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg if (HttpClient::Response Result = Http.Get(Url, HttpClient::Accept(ZenContentType::kJSON))) { ZEN_CONSOLE("{}", Result.ToText()); - return 0; } else { Result.ThrowError("failed to fetch info"sv); - return 1; } } @@ -745,7 +729,7 @@ CreateProjectCommand::CreateProjectCommand() CreateProjectCommand::~CreateProjectCommand() = default; -int +void CreateProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); @@ -754,19 +738,19 @@ CreateProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } if (m_ProjectId.empty()) { - throw OptionParseException("Project name must be given"); + throw OptionParseException("'--project' is required", m_Options.help()); } HttpClient Http(m_HostName); @@ -777,8 +761,7 @@ CreateProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a { if (HttpClient::Response Result = Http.Get(Url, HttpClient::Accept(ZenContentType::kJSON))) { - ZEN_CONSOLE_ERROR("Project already exists.\n{}", Result.ToText()); - return 1; + throw std::runtime_error(fmt::format("Project already exists.\n{}", Result.ToText())); } } @@ -793,12 +776,10 @@ CreateProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a : Http.Post(Url, Payload, HttpClient::Accept(ZenContentType::kText))) { ZEN_CONSOLE("{}", Result); - return 0; } else { Result.ThrowError("failed to create project"sv); - return 1; } } @@ -817,7 +798,7 @@ CreateOplogCommand::CreateOplogCommand() CreateOplogCommand::~CreateOplogCommand() = default; -int +void CreateOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); @@ -826,31 +807,31 @@ CreateOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } if (m_ProjectId.empty()) { - throw OptionParseException("project name must be specified"); + throw OptionParseException("'--project' is required", m_Options.help()); } HttpClient Http(m_HostName); m_ProjectId = ResolveProject(Http, m_ProjectId); if (m_ProjectId.empty()) { - return 1; + throw std::runtime_error("Project can not be found"); } if (m_OplogId.empty()) { - throw OptionParseException("oplog name must be specified"); + throw OptionParseException("'--oplog' is required", m_Options.help()); } std::string Url = fmt::format("/prj/{}/oplog/{}", m_ProjectId, m_OplogId); @@ -858,8 +839,7 @@ CreateOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg { if (HttpClient::Response Result = Http.Get(Url, HttpClient::Accept(ZenContentType::kJSON))) { - ZEN_CONSOLE("Oplog already exists.\n{}", Result.ToText()); - return 1; + throw std::runtime_error(fmt::format("Oplog already exists.\n{}", Result.ToText())); } } @@ -873,12 +853,10 @@ CreateOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg : Http.Post(Url, OplogPayload, HttpClient::Accept(ZenContentType::kText))) { ZEN_CONSOLE("{}", Result); - return 0; } else { Result.ThrowError("failed to create oplog"sv); - return 1; } } @@ -1014,7 +992,7 @@ ExportOplogCommand::~ExportOplogCommand() { } -int +void ExportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { using namespace std::literals; @@ -1023,32 +1001,32 @@ ExportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } if (m_ProjectName.empty()) { - throw OptionParseException("project name must be specified"); + throw OptionParseException("'--project' is required", m_Options.help()); } HttpClient Http(m_HostName); m_ProjectName = ResolveProject(Http, m_ProjectName); if (m_ProjectName.empty()) { - return 1; + throw std::runtime_error("Project can not be found"); } m_OplogName = ResolveOplog(Http, m_ProjectName, m_OplogName); if (m_OplogName.empty()) { - return 1; + throw std::runtime_error("Oplog can not be found"); } size_t TargetCount = 0; @@ -1060,19 +1038,23 @@ ExportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg { if (TargetCount == 0) { - throw OptionParseException("an export target must be specified"); + throw OptionParseException("'--cloud', '--builds', '--zen' or '--file' is required", m_Options.help()); } else { - throw OptionParseException("a single export target must be specified"); + throw OptionParseException("'--cloud', '--builds', '--zen' or '--file' are conflicting", m_Options.help()); } } if (!m_CloudUrl.empty()) { - if (m_JupiterNamespace.empty() || m_JupiterBucket.empty()) + if (m_JupiterNamespace.empty()) { - throw OptionParseException("Options for cloud target are missing"); + throw OptionParseException("'--namespace' is required", m_Options.help()); + } + if (m_JupiterBucket.empty()) + { + throw OptionParseException("'--bucket' is required", m_Options.help()); } if (m_CloudKey.empty()) { @@ -1087,19 +1069,21 @@ ExportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg { if (m_JupiterNamespace.empty()) { - throw OptionParseException("Namespace for builds target it missing"); + throw OptionParseException("'--namespace' is required", m_Options.help()); } if (m_JupiterNamespace.empty() || m_JupiterBucket.empty()) { - throw OptionParseException("Bucket for builds target it missing"); + throw OptionParseException("'--bucket' is required", m_Options.help()); } if (m_BuildsMetadataPath.empty() && m_BuildsMetadata.empty()) { - throw OptionParseException("Options for builds target metadata are missing"); + throw OptionParseException("'--metadata' or --'metadata-path' is required", m_Options.help()); } if (!m_BuildsMetadataPath.empty() && !m_BuildsMetadata.empty()) { - throw OptionParseException("Conflicting options for builds target metadata"); + throw OptionParseException( + fmt::format("'--metadata' ('{}') conflicts with --'metadata-path' ('{}')", m_BuildsMetadata, m_BuildsMetadataPath), + m_Options.help()); } if (m_BuildsId.empty()) { @@ -1141,7 +1125,6 @@ ExportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg if (!Result) { Result.ThrowError("failed deleting existing zen remote oplog"sv); - return 1; } CreateOplog = true; } @@ -1153,7 +1136,6 @@ ExportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg else { Result.ThrowError("failed checking zen remote oplog"sv); - return 1; } if (CreateOplog) @@ -1162,7 +1144,6 @@ ExportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg if (HttpClient::Response Result = TargetHttp.Post(Url); !Result) { Result.ThrowError("failed creating zen remote oplog"sv); - return 1; } } } @@ -1341,36 +1322,24 @@ ExportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg ZEN_CONSOLE("Saving oplog '{}/{}' from '{}' to {}", m_ProjectName, m_OplogName, m_HostName, TargetDescription); - try + if (m_Async) { - if (m_Async) + if (HttpClient::Response Result = Http.Post(fmt::format("/prj/{}/oplog/{}/rpc", m_ProjectName, m_OplogName), + std::move(Payload), + HttpClient::Accept(ZenContentType::kJSON)); + Result) { - if (HttpClient::Response Result = Http.Post(fmt::format("/prj/{}/oplog/{}/rpc", m_ProjectName, m_OplogName), - std::move(Payload), - HttpClient::Accept(ZenContentType::kJSON)); - Result) - { - ZEN_CONSOLE("{}", Result.ToText()); - } - else - { - Result.ThrowError("failed requesting loading oplog export"sv); - } + ZEN_CONSOLE("{}", Result.ToText()); } else { - ExecuteAsyncOperation(Http, - fmt::format("/prj/{}/oplog/{}/rpc", m_ProjectName, m_OplogName), - std::move(Payload), - m_PlainProgress); + Result.ThrowError("failed requesting loading oplog export"sv); } } - catch (const AsyncJobError& Ex) + else { - ZEN_CONSOLE_ERROR("Oplog export failed: '{}'", Ex.what()); - return Ex.m_ReturnCode; + ExecuteAsyncOperation(Http, fmt::format("/prj/{}/oplog/{}/rpc", m_ProjectName, m_OplogName), std::move(Payload), m_PlainProgress); } - return 0; } //////////////////////////// @@ -1462,7 +1431,7 @@ ImportOplogCommand::~ImportOplogCommand() { } -int +void ImportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { using namespace std::literals; @@ -1471,31 +1440,31 @@ ImportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } if (m_ProjectName.empty()) { - throw OptionParseException("Project name must be given"); + throw OptionParseException("'--project' is required", m_Options.help()); } if (m_OplogName.empty()) { - throw OptionParseException("Oplog name must be given"); + throw OptionParseException("'--oplog' is required", m_Options.help()); } HttpClient Http(m_HostName); m_ProjectName = ResolveProject(Http, m_ProjectName); if (m_ProjectName.empty()) { - return 1; + throw std::runtime_error("Project can not be found"); } size_t TargetCount = 0; @@ -1503,20 +1472,24 @@ ImportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg TargetCount += m_BuildsUrl.empty() ? 0 : 1; TargetCount += m_ZenUrl.empty() ? 0 : 1; TargetCount += m_FileDirectoryPath.empty() ? 0 : 1; - if (TargetCount != 1) + if (TargetCount == 0) { - throw OptionParseException("Provide one source only"); + throw OptionParseException("'--cloud', '--builds', '--zen' or '--file' is required", m_Options.help()); + } + else if (TargetCount > 1) + { + throw OptionParseException("'--cloud', '--builds', '--zen' or '--file' are conflicting", m_Options.help()); } if (!m_CloudUrl.empty()) { if (m_JupiterNamespace.empty()) { - throw OptionParseException("Namespace option for cloud source is missing"); + throw OptionParseException("--'namespace' is required", m_Options.help()); } if (m_JupiterBucket.empty()) { - throw OptionParseException("Bucket option for cloud source is missing"); + throw OptionParseException("--'bucket' is required", m_Options.help()); } if (m_CloudKey.empty()) { @@ -1531,15 +1504,15 @@ ImportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg { if (m_JupiterNamespace.empty()) { - throw OptionParseException("Namespace option for builds source is missing"); + throw OptionParseException("'--namespace' is required", m_Options.help()); } if (m_JupiterBucket.empty()) { - throw OptionParseException("Bucket option for builds source is missing"); + throw OptionParseException("'--bucket' is required", m_Options.help()); } if (m_BuildsId.empty()) { - throw OptionParseException("Build id option for builds source is missing"); + throw OptionParseException("'--build-id' is required", m_Options.help()); } } @@ -1577,7 +1550,6 @@ ImportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg else if (!IsHttpSuccessCode(Result.StatusCode)) { Result.ThrowError("failed checking oplog"sv); - return 1; } if (CreateOplog) @@ -1591,7 +1563,6 @@ ImportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg if (HttpClient::Response Result = Http.Post(Url, OplogPayload); !Result) { Result.ThrowError("failed creating oplog"sv); - return 1; } } @@ -1688,36 +1659,24 @@ ImportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg ZEN_CONSOLE("Loading oplog '{}/{}' from '{}' to {}", m_ProjectName, m_OplogName, SourceDescription, m_HostName); - try + if (m_Async) { - if (m_Async) + if (HttpClient::Response Result = Http.Post(fmt::format("/prj/{}/oplog/{}/rpc", m_ProjectName, m_OplogName), + std::move(Payload), + HttpClient::Accept(ZenContentType::kJSON)); + Result) { - if (HttpClient::Response Result = Http.Post(fmt::format("/prj/{}/oplog/{}/rpc", m_ProjectName, m_OplogName), - std::move(Payload), - HttpClient::Accept(ZenContentType::kJSON)); - Result) - { - ZEN_CONSOLE("{}", Result.ToText()); - } - else - { - Result.ThrowError("failed requesting loading oplog import"sv); - } + ZEN_CONSOLE("{}", Result.ToText()); } else { - ExecuteAsyncOperation(Http, - fmt::format("/prj/{}/oplog/{}/rpc", m_ProjectName, m_OplogName), - std::move(Payload), - m_PlainProgress); + Result.ThrowError("failed requesting loading oplog import"sv); } } - catch (const AsyncJobError& Ex) + else { - ZEN_CONSOLE_ERROR("Oplog export failed: '{}'", Ex.what()); - return Ex.m_ReturnCode; + ExecuteAsyncOperation(Http, fmt::format("/prj/{}/oplog/{}/rpc", m_ProjectName, m_OplogName), std::move(Payload), m_PlainProgress); } - return 0; } //////////////////////////// @@ -1736,7 +1695,7 @@ SnapshotOplogCommand::~SnapshotOplogCommand() { } -int +void SnapshotOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { using namespace std::literals; @@ -1745,34 +1704,33 @@ SnapshotOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } HttpClient Http(m_HostName); if (m_ProjectName.empty()) { - throw OptionParseException("Project name must be given"); - return 1; + throw OptionParseException("'--project' is required", m_Options.help()); } m_ProjectName = ResolveProject(Http, m_ProjectName); if (m_ProjectName.empty()) { - return 1; + throw std::runtime_error("Project can not be found"); } m_OplogName = ResolveOplog(Http, m_ProjectName, m_OplogName); if (m_OplogName.empty()) { - return 1; + throw std::runtime_error("Oplog can not be found"); } IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) { Writer.AddString("method"sv, "snapshot"sv); }); @@ -1782,12 +1740,10 @@ SnapshotOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a Http.Post(fmt::format("/prj/{}/oplog/{}/rpc", m_ProjectName, m_OplogName), Payload, HttpClient::Accept(ZenContentType::kJSON))) { ZEN_CONSOLE("{}", Result); - return 0; } else { Result.ThrowError("failed to create project"sv); - return 1; } } @@ -1803,33 +1759,31 @@ ProjectStatsCommand::~ProjectStatsCommand() { } -int +void ProjectStatsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } HttpClient Http(m_HostName); if (HttpClient::Response Result = Http.Get("/stats/prj", HttpClient::Accept(ZenContentType::kJSON))) { ZEN_CONSOLE("{}", Result.ToText()); - return 0; } else { Result.ThrowError("failed to get project stats"sv); - return 1; } } @@ -1857,21 +1811,21 @@ ProjectOpDetailsCommand::~ProjectOpDetailsCommand() { } -int +void ProjectOpDetailsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } HttpClient Http(m_HostName); @@ -1879,13 +1833,13 @@ ProjectOpDetailsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char* m_ProjectName = ResolveProject(Http, m_ProjectName); if (m_ProjectName.empty()) { - return 1; + throw std::runtime_error("Project can not be found"); } m_OplogName = ResolveOplog(Http, m_ProjectName, m_OplogName); if (m_OplogName.empty()) { - return 1; + throw std::runtime_error("Oplog can not be found"); } ExtendableStringBuilder<128> Url; @@ -1915,12 +1869,10 @@ ProjectOpDetailsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char* {"csv", m_CSV ? "true" : "false"}})) { ZEN_CONSOLE("{}", Result.ToText()); - return 0; } else { Result.ThrowError("failed to get project details"sv); - return 1; } } @@ -1974,21 +1926,21 @@ OplogMirrorCommand::~OplogMirrorCommand() { } -int +void OplogMirrorCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } HttpClient Http(m_HostName); @@ -1996,18 +1948,18 @@ OplogMirrorCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg m_ProjectName = ResolveProject(Http, m_ProjectName); if (m_ProjectName.empty()) { - return 1; + throw std::runtime_error("Project can not be found"); } m_OplogName = ResolveOplog(Http, m_ProjectName, m_OplogName); if (m_OplogName.empty()) { - return 1; + throw std::runtime_error("Oplog can not be found"); } if (m_MirrorRootPath.empty()) { - throw OptionParseException("a target path must be specified"); + throw OptionParseException("'--target' is required", m_Options.help()); } Oid ChunkIdFilter = Oid::Zero; @@ -2016,7 +1968,7 @@ OplogMirrorCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg ChunkIdFilter = Oid::TryFromHexString(m_ChunkIdFilter); if (ChunkIdFilter == Oid::Zero) { - throw OptionParseException("chunkid must be an Oid hex string"); + throw OptionParseException(fmt::format("'--chunk' ('{}') is malformed", m_ChunkIdFilter), m_Options.help()); } } @@ -2206,25 +2158,20 @@ OplogMirrorCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg if (AbortFlag) { - // Error has already been reported by async code - return 1; + throw std::runtime_error("Failed top mirror oplog"); } } else { - ZEN_CONSOLE_ERROR("Unknown format response to oplog entries request"); + throw std::runtime_error("Unknown format response to oplog entries request"); } } else { Response.ThrowError("oplog entries fetch failed"); - - return 1; } ZEN_CONSOLE("mirrored {} files from {} oplog entries successfully", FileCount.load(), OplogEntryCount); - - return 0; } //////////////////////////// @@ -2244,21 +2191,21 @@ OplogValidateCommand::~OplogValidateCommand() { } -int +void OplogValidateCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } HttpClient Http(m_HostName); @@ -2266,13 +2213,13 @@ OplogValidateCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a m_ProjectName = ResolveProject(Http, m_ProjectName); if (m_ProjectName.empty()) { - return 1; + throw std::runtime_error("Project can not be found"); } m_OplogName = ResolveOplog(Http, m_ProjectName, m_OplogName); if (m_OplogName.empty()) { - return 1; + throw std::runtime_error("Oplog can not be found"); } std::string Url = fmt::format("/prj/{}/oplog/{}/validate", m_ProjectName, m_OplogName); @@ -2280,15 +2227,11 @@ OplogValidateCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a if (HttpClient::Response Result = Http.Post(Url, HttpClient::Accept(ZenContentType::kJSON))) { ZEN_CONSOLE("{}", Result.ToText()); - return 0; } else { Result.ThrowError("failed to get validate project oplog"sv); - return 1; } - - return 0; } } // namespace zen diff --git a/src/zen/cmds/projectstore_cmd.h b/src/zen/cmds/projectstore_cmd.h index 0d24d8529..70b336650 100644 --- a/src/zen/cmds/projectstore_cmd.h +++ b/src/zen/cmds/projectstore_cmd.h @@ -17,7 +17,7 @@ public: DropProjectCommand(); ~DropProjectCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -33,7 +33,7 @@ class ProjectInfoCommand : public ProjectStoreCommand public: ProjectInfoCommand(); ~ProjectInfoCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -49,7 +49,7 @@ public: CreateProjectCommand(); ~CreateProjectCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -69,7 +69,7 @@ public: CreateOplogCommand(); ~CreateOplogCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -87,7 +87,7 @@ public: ExportOplogCommand(); ~ExportOplogCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -142,7 +142,7 @@ public: ImportOplogCommand(); ~ImportOplogCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -189,7 +189,7 @@ public: SnapshotOplogCommand(); ~SnapshotOplogCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -204,7 +204,7 @@ class ProjectStatsCommand : public ProjectStoreCommand public: ProjectStatsCommand(); ~ProjectStatsCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -217,7 +217,7 @@ class ProjectOpDetailsCommand : public ProjectStoreCommand public: ProjectOpDetailsCommand(); ~ProjectOpDetailsCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -237,7 +237,7 @@ class OplogMirrorCommand : public ProjectStoreCommand public: OplogMirrorCommand(); ~OplogMirrorCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -258,7 +258,7 @@ class OplogValidateCommand : public ProjectStoreCommand public: OplogValidateCommand(); ~OplogValidateCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: diff --git a/src/zen/cmds/rpcreplay_cmd.cpp b/src/zen/cmds/rpcreplay_cmd.cpp index 6c26eaf23..4e672eea5 100644 --- a/src/zen/cmds/rpcreplay_cmd.cpp +++ b/src/zen/cmds/rpcreplay_cmd.cpp @@ -12,6 +12,7 @@ #include <zencore/stream.h> #include <zencore/timer.h> #include <zencore/workthreadpool.h> +#include <zenhttp/httpclient.h> #include <zenhttp/httpcommon.h> #include <zenhttp/packageformat.h> #include <zenutil/cache/rpcrecording.h> @@ -26,6 +27,54 @@ ZEN_THIRD_PARTY_INCLUDES_END namespace zen { +namespace { + // TODO: Convert direct use of cpr to HttpClient + + std::string FormatHttpResponse(const cpr::Response& Response) + { + if (Response.error.code != cpr::ErrorCode::OK) + { + if (Response.error.message.empty()) + { + return fmt::format("Request '{}' failed, error code {}", Response.url.str(), static_cast<int>(Response.error.code)); + } + return fmt::format("Request '{}' failed. Reason: '{}' ({})", + Response.url.str(), + Response.error.message, + static_cast<int>(Response.error.code)); + } + + std::string Content; + if (auto It = Response.header.find("Content-Type"); It != Response.header.end()) + { + zen::HttpContentType ContentType = zen::ParseContentType(It->second); + if (ContentType == zen::HttpContentType::kText) + { + Content = Response.text; + } + else if (ContentType == zen::HttpContentType::kJSON) + { + Content = fmt::format("\n{}", Response.text); + } + else if (!Response.text.empty()) + { + Content = fmt::format("[{}]", MapContentTypeToString(ContentType)); + } + } + + std::string_view ResponseString = zen::ReasonStringForHttpResultCode( + Response.status_code == static_cast<long>(zen::HttpResponseCode::NoContent) ? static_cast<long>(zen::HttpResponseCode::OK) + : Response.status_code); + if (Content.empty()) + { + return std::string(ResponseString); + } + + return fmt::format("{}: {}", ResponseString, Content); + } + +} // namespace + using namespace std::literals; RpcStartRecordingCommand::RpcStartRecordingCommand() @@ -39,33 +88,37 @@ RpcStartRecordingCommand::RpcStartRecordingCommand() RpcStartRecordingCommand::~RpcStartRecordingCommand() = default; -int +void RpcStartRecordingCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions, argc, argv); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw zen::OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } if (m_RecordingPath.empty()) { - throw zen::OptionParseException("Rpc start recording command requires a path"); + throw OptionParseException("'--path' is required", m_Options.help()); } - cpr::Session Session; - Session.SetUrl(fmt::format("{}/z$/exec$/start-recording"sv, m_HostName)); - Session.SetParameters({{"path", m_RecordingPath}}); - cpr::Response Response = Session.Post(); - ZEN_CONSOLE("{}", FormatHttpResponse(Response)); - return MapHttpToCommandReturnCode(Response); + HttpClient Http(m_HostName); + if (HttpClient::Response Response = + Http.Post("/z$/exec$/start-recording"sv, HttpClient::KeyValueMap{}, HttpClient::KeyValueMap({{"path", m_RecordingPath}}))) + { + ZEN_CONSOLE("{}", Response.ToText()); + } + else + { + Response.ThrowError("Failed to start recording"); + } } //////////////////////////////////////////////////// @@ -78,28 +131,32 @@ RpcStopRecordingCommand::RpcStopRecordingCommand() RpcStopRecordingCommand::~RpcStopRecordingCommand() = default; -int +void RpcStopRecordingCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions, argc, argv); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw zen::OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } - cpr::Session Session; - Session.SetUrl(fmt::format("{}/z$/exec$/stop-recording"sv, m_HostName)); - cpr::Response Response = Session.Post(); - ZEN_CONSOLE("{}", FormatHttpResponse(Response)); - return MapHttpToCommandReturnCode(Response); + HttpClient Http(m_HostName); + if (HttpClient::Response Response = Http.Post("/z$/exec$/stop-recording"sv)) + { + ZEN_CONSOLE("{}", Response.ToText()); + } + else + { + Response.ThrowError("Failed to stop recording"); + } } //////////////////////////////////////////////////// @@ -174,26 +231,26 @@ RpcReplayCommand::RpcReplayCommand() RpcReplayCommand::~RpcReplayCommand() = default; -int +void RpcReplayCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions, argc, argv); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw zen::OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } if (m_RecordingPath.empty()) { - throw zen::OptionParseException("Rpc replay command requires a path"); + throw OptionParseException("'--path' is required", m_Options.help()); } if (!IsDir(m_RecordingPath)) @@ -207,12 +264,18 @@ RpcReplayCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (m_OnHost) { - cpr::Session Session; - Session.SetUrl(fmt::format("{}/z$/exec$/replay-recording"sv, m_HostName)); - Session.SetParameters({{"path", m_RecordingPath}, {"thread-count", fmt::format("{}", m_ThreadCount)}}); - cpr::Response Response = Session.Post(); - ZEN_CONSOLE("{}", FormatHttpResponse(Response)); - return MapHttpToCommandReturnCode(Response); + HttpClient Http(m_HostName); + if (HttpClient::Response Response = + Http.Post("/z$/exec$/replay-recording"sv, + HttpClient::KeyValueMap{}, + HttpClient::KeyValueMap({{"path", m_RecordingPath}, {"thread-count", fmt::format("{}", m_ThreadCount)}}))) + { + ZEN_CONSOLE("{}", Response.ToText()); + } + else + { + Response.ThrowError("Failed to start replay"); + } } std::unique_ptr<cache::IRpcRequestReplayer> Replayer = cache::MakeDiskRequestReplayer(m_RecordingPath, true); @@ -251,7 +314,7 @@ RpcReplayCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ZEN_CONSOLE("Waiting for worker processes..."); Sleep(1000); } - return 0; + return; } else { @@ -478,8 +541,6 @@ RpcReplayCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) NiceByteRate(Received, ElapsedMS), NiceTimeSpanMs(ElapsedMS), NiceTimeSpanMs(TotalTimer.GetElapsedTimeMs())); - - return 0; } } // namespace zen diff --git a/src/zen/cmds/rpcreplay_cmd.h b/src/zen/cmds/rpcreplay_cmd.h index 42cdd4ac1..a6363b614 100644 --- a/src/zen/cmds/rpcreplay_cmd.h +++ b/src/zen/cmds/rpcreplay_cmd.h @@ -12,7 +12,7 @@ public: RpcStartRecordingCommand(); ~RpcStartRecordingCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -27,7 +27,7 @@ public: RpcStopRecordingCommand(); ~RpcStopRecordingCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -41,7 +41,7 @@ public: RpcReplayCommand(); ~RpcReplayCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: diff --git a/src/zen/cmds/run_cmd.cpp b/src/zen/cmds/run_cmd.cpp index 309b8996a..00ab16fe5 100644 --- a/src/zen/cmds/run_cmd.cpp +++ b/src/zen/cmds/run_cmd.cpp @@ -57,32 +57,32 @@ RunCommand::~RunCommand() { } -int +void RunCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { - ZEN_UNUSED(GlobalOptions); - if (!ParseOptions(argc, argv)) { - return 0; + return; } // Validate arguments if (GlobalOptions.PassthroughArgV.empty() || GlobalOptions.PassthroughArgV[0].empty()) - throw OptionParseException("No command specified. The command to run is passed in after a double dash ('--') on the command line"); + throw OptionParseException("No command specified. The command to run is passed in after a double dash ('--') on the command line", + m_Options.help()); if (m_RunCount < 0) - throw OptionParseException("Invalid count specified"); + throw OptionParseException(fmt::format("'--count' ('{}') is invalid", m_RunCount), m_Options.help()); if (m_RunTime < -1 || m_RunTime == 0) - throw OptionParseException("Invalid run time specified"); + throw OptionParseException(fmt::format("'--time' ('{}') is invalid", m_RunTime), m_Options.help()); if (m_MaxBaseDirectoryCount < 0) - throw OptionParseException("Invalid directory count specified"); + throw OptionParseException(fmt::format("'--max-dirs' ('{}') is invalid", m_MaxBaseDirectoryCount), m_Options.help()); if (m_RunTime > 0 && m_RunCount > 0) - throw OptionParseException("Specify either time or count, not both"); + throw OptionParseException(fmt::format("'--time' ('{}') conflicts with '--count' ('{}') ", m_RunTime, m_RunCount), + m_Options.help()); if (m_RunCount == 0) m_RunCount = 1; @@ -190,8 +190,6 @@ RunCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { fmt::print("run complete, no error exit code\n", m_RunCount); } - - return 0; } } // namespace zen diff --git a/src/zen/cmds/run_cmd.h b/src/zen/cmds/run_cmd.h index f6512a4e8..570a2e63a 100644 --- a/src/zen/cmds/run_cmd.h +++ b/src/zen/cmds/run_cmd.h @@ -12,7 +12,7 @@ public: RunCommand(); ~RunCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } virtual ZenCmdCategory& CommandCategory() const override { return g_UtilitiesCategory; } diff --git a/src/zen/cmds/serve_cmd.cpp b/src/zen/cmds/serve_cmd.cpp index cfaa12bc0..49389bcdf 100644 --- a/src/zen/cmds/serve_cmd.cpp +++ b/src/zen/cmds/serve_cmd.cpp @@ -34,19 +34,19 @@ ServeCommand::~ServeCommand() { } -int +void ServeCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } if (m_ProjectName.empty()) { - throw zen::OptionParseException("command requires a project"); + throw OptionParseException("'--project' is required", m_Options.help()); } if (m_OplogName.empty()) @@ -58,18 +58,18 @@ ServeCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } else { - throw zen::OptionParseException("command requires an oplog"); + throw OptionParseException("'--oplog' is required", m_Options.help()); } } if (m_RootPath.empty()) { - throw zen::OptionParseException("command requires a root path"); + throw OptionParseException("'--path' is required", m_Options.help()); } if (!IsDir(m_RootPath)) { - throw zen::OptionParseException(fmt::format("path must exist and must be a directory: '{}'", m_RootPath)); + throw std::runtime_error(fmt::format("'--path' ('{}') must exist and must be a directory", m_RootPath)); } uint16_t ServerPort = 0; @@ -95,7 +95,7 @@ ServeCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_CONSOLE_ERROR("Failed to spawn server on port {}: '{}'", ServerPort, Ex.what()); - throw zen::OptionParseException("unable to resolve server specification (even after spawning server)"); + throw std::runtime_error("Unable to resolve server specification (even after spawning server)"); } } else @@ -197,8 +197,7 @@ ServeCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (auto NewProjectResponse = Client.Post(ProjectUri, Project.Save()); !NewProjectResponse) { - // TODO: include details - throw std::runtime_error("failed to create project"); + NewProjectResponse.ThrowError("Failed to create project"); } } @@ -212,8 +211,7 @@ ServeCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (auto NewOplogResponse = Client.Post(ProjectOplogUri, Oplog.Save()); !NewOplogResponse) { - // TODO: include details - throw std::runtime_error("failed to create oplog"); + NewOplogResponse.ThrowError("Failed to create oplog"); } } @@ -233,8 +231,6 @@ ServeCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) #if ZEN_PLATFORM_WINDOWS _getch(); // TEMPORARY HACK #endif - - return 0; } } // namespace zen diff --git a/src/zen/cmds/serve_cmd.h b/src/zen/cmds/serve_cmd.h index 007038d84..ac74981f2 100644 --- a/src/zen/cmds/serve_cmd.h +++ b/src/zen/cmds/serve_cmd.h @@ -14,7 +14,7 @@ public: ServeCommand(); ~ServeCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: diff --git a/src/zen/cmds/service_cmd.cpp b/src/zen/cmds/service_cmd.cpp index 398bbfc83..cf3a0bcd0 100644 --- a/src/zen/cmds/service_cmd.cpp +++ b/src/zen/cmds/service_cmd.cpp @@ -52,7 +52,7 @@ namespace { return fRet; } - int WinRelaunchElevated() + void WinRelaunchElevated() { TCHAR CurrentDir[4096]; GetCurrentDirectory(4096, CurrentDir); @@ -75,27 +75,26 @@ namespace { shExInfo.nShow = SW_SHOW; shExInfo.hInstApp = 0; - DWORD ReturnCode = 1; + DWORD ProcessReturnCode = 1; if (ShellExecuteEx(&shExInfo)) { WaitForSingleObject(shExInfo.hProcess, INFINITE); - GetExitCodeProcess(shExInfo.hProcess, &ReturnCode); + GetExitCodeProcess(shExInfo.hProcess, &ProcessReturnCode); CloseHandle(shExInfo.hProcess); - if (ReturnCode == 0) + if (ProcessReturnCode == 0) { ZEN_CONSOLE("Elevated execution completed successfully."); } else { - ZEN_CONSOLE("Elevated execution completed unsuccessfully, return code: '{}'.", ReturnCode); + throw ErrorWithReturnCode(fmt::format("Elevated execution completed unsuccessfully, return code: '{}'.", ProcessReturnCode), + (int)ProcessReturnCode); } } else { - ZEN_CONSOLE("Failed to run elevated, operation did not complete."); - ReturnCode = DWORD(-1); + ThrowLastError("Failed to run elevated, operation did not complete"); } - return (int)ReturnCode; } #else // ZEN_PLATFORM_WINDOWS @@ -104,25 +103,23 @@ namespace { #endif // ZEN_PLATFORM_WINDOWS - int RunElevated(bool AllowElevation) + void RunElevated(bool AllowElevation) { #if ZEN_PLATFORM_WINDOWS if (AllowElevation) { - return WinRelaunchElevated(); + WinRelaunchElevated(); } else { - ZEN_CONSOLE( + throw std::runtime_error(fmt::format( "This command requires elevated priviliges. Run command with elevated priviliges or add '--allow-elevation' command line " - "option."); - return 1; + "option.")); } #endif // ZEN_PLATFORM_WINDOWS #if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC ZEN_UNUSED(AllowElevation); - ZEN_CONSOLE("This command requires elevated priviliges. Run the command with `sudo`"); - return 1; + throw std::runtime_error(fmt::format("This command requires elevated priviliges. Run the command with `sudo`")); #endif // ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC } @@ -295,11 +292,9 @@ enum class ServiceStatusReturnCode UnknownError }; -int +void ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { - ZEN_UNUSED(GlobalOptions); - using namespace std::literals; std::vector<char*> SubCommandArguments; @@ -307,17 +302,17 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) int ParentCommandArgCount = GetSubCommand(m_Options, argc, argv, m_SubCommands, SubOption, SubCommandArguments); if (!ParseOptions(ParentCommandArgCount, argv)) { - return 0; + return; } if (SubOption == nullptr) { - throw zen::OptionParseException("command verb is missing"); + throw OptionParseException("'verb' option is required", m_Options.help()); } if (!ParseOptions(*SubOption, gsl::narrow<int>(SubCommandArguments.size()), SubCommandArguments.data())) { - return 0; + return; } if (SubOption == &m_StatusOptions) @@ -326,18 +321,15 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) std::error_code Ec = QueryInstalledService(m_ServiceName, Info); if (Ec) { - ZEN_CONSOLE("Can't get information about service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); - return gsl::narrow<int>(ServiceStatusReturnCode::UnknownError); + throw std::runtime_error(fmt::format("Can't get information about service '{}'. Reason: '{}'", m_ServiceName, Ec.message())); } if (Info.Status == ServiceStatus::NotInstalled) { - ZEN_CONSOLE("Service '{}' is not installed", m_ServiceName); - return gsl::narrow<int>(ServiceStatusReturnCode::NotInstalled); + throw std::runtime_error(fmt::format("Service '{}' is not installed", m_ServiceName)); } else if (Info.Status != ServiceStatus::Running) { - ZEN_CONSOLE("Service '{}' is not running", m_ServiceName); - return gsl::narrow<int>(ServiceStatusReturnCode::NotRunning); + throw std::runtime_error(fmt::format("Service '{}' is not running", m_ServiceName)); } else { @@ -349,7 +341,8 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { if (!IsElevated()) { - return RunElevated(m_AllowElevation); + RunElevated(m_AllowElevation); + return; } ServiceInfo Info; @@ -363,12 +356,10 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) Ec = StopService(m_ServiceName); if (Ec) { - ZEN_CONSOLE("Failed to stop service '{}' using '{}'. Reason: '{}'", - m_ServiceName, - m_ServerExecutable, - Ec.message()); - - return 1; + throw std::runtime_error(fmt::format("Failed to stop service '{}' using '{}'. Reason: '{}'", + m_ServiceName, + m_ServerExecutable, + Ec.message())); } int Timeout = 30000; // Wait up to 30 seconds for the service to fully stop before uninstalling @@ -377,8 +368,7 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) Ec = QueryInstalledService(m_ServiceName, Info); if (Ec) { - ZEN_CONSOLE("Failed to wait for service to stop: '{}'", Ec.message()); - return 1; + throw std::runtime_error(fmt::format("Failed to wait for service to stop: '{}'", Ec.message())); } if (Info.Status == ServiceStatus::Stopped) @@ -392,26 +382,23 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (Info.Status != ServiceStatus::Stopped) { - ZEN_CONSOLE("Timed out waiting for service to stop"); - return 1; + throw std::runtime_error("Timed out waiting for service to stop"); } } Ec = UninstallService(m_ServiceName); if (Ec) { - ZEN_CONSOLE("Failed to uninstall running service '{}' using '{}'. Reason: '{}'", - m_ServiceName, - m_ServerExecutable, - Ec.message()); - - return 1; + throw std::runtime_error(fmt::format("Failed to uninstall running service '{}' using '{}'. Reason: '{}'", + m_ServiceName, + m_ServerExecutable, + Ec.message())); } } else { ZEN_CONSOLE("Service '{}' already installed:\n{}", m_ServiceName, FmtServiceInfo(Info, " ")); - return 1; + return; } } if (m_ServerExecutable.empty()) @@ -426,7 +413,7 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { if (m_InstallPath.empty()) { - throw zen::OptionParseException("--full requires --install-path to be specified"); + throw OptionParseException("'--full' requires '--install-path'", SubOption->help()); } std::filesystem::path ExePath = zen::GetRunningExecutablePath(); @@ -451,8 +438,7 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (!std::filesystem::is_directory(m_InstallPath) && !CreateDirectories(m_InstallPath)) { - ZEN_CONSOLE("Unable to create install directory '{}'", m_InstallPath); - return 1; + throw std::runtime_error(fmt::format("Unable to create install directory '{}'", m_InstallPath)); } for (const std::filesystem::path& File : FilesToCopy) @@ -461,8 +447,7 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (!CopyFile(File, Destination, {.EnableClone = false})) { - ZEN_CONSOLE("Failed to copy '{}' to '{}'", File, Destination); - return 1; + throw std::runtime_error(fmt::format("Failed to copy '{}' to '{}'", File, Destination)); } ZEN_INFO("Copied '{}' to '{}'", File, Destination); @@ -487,8 +472,8 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) }); if (Ec) { - ZEN_CONSOLE("Failed to install service '{}' using '{}' . Reason: '{}'", m_ServiceName, m_ServerExecutable, Ec.message()); - return 1; + throw std::runtime_error( + fmt::format("Failed to install service '{}' using '{}' . Reason: '{}'", m_ServiceName, m_ServerExecutable, Ec.message())); } ZEN_CONSOLE("Installed service '{}' using '{}' successfully", m_ServiceName, m_ServerExecutable); @@ -497,8 +482,7 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) Ec = StartService(m_ServiceName); if (Ec) { - ZEN_CONSOLE("Failed to start service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); - return 1; + throw std::runtime_error(fmt::format("Failed to start service '{}'. Reason: '{}'", m_ServiceName, Ec.message())); } } } @@ -509,30 +493,28 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) std::error_code Ec = QueryInstalledService(m_ServiceName, Info); if (Ec) { - ZEN_CONSOLE("Failed to inspect installed service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); - return 1; + throw std::runtime_error(fmt::format("Failed to inspect installed service '{}'. Reason: '{}'", m_ServiceName, Ec.message())); } if (Info.Status == ServiceStatus::NotInstalled) { ZEN_CONSOLE("Service '{}' is not installed", m_ServiceName); - return 0; + return; } if (Info.Status != ServiceStatus::Stopped) { - ZEN_CONSOLE("Service '{}' is running, stop before uninstalling", m_ServiceName); - return 0; + throw std::runtime_error(fmt::format("Service '{}' is running, stop before uninstalling", m_ServiceName)); } if (!IsElevated()) { - return RunElevated(m_AllowElevation); + RunElevated(m_AllowElevation); + return; } Ec = UninstallService(m_ServiceName); if (Ec) { - ZEN_CONSOLE("Failed to uninstall service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); - return 1; + throw std::runtime_error(fmt::format("Failed to uninstall service '{}'. Reason: '{}'", m_ServiceName, Ec.message())); } ZEN_CONSOLE("Uninstalled service {} successfully", m_ServiceName); } @@ -543,30 +525,28 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) std::error_code Ec = QueryInstalledService(m_ServiceName, Info); if (Ec) { - ZEN_CONSOLE("Failed to inspect installed service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); - return 1; + throw std::runtime_error(fmt::format("Failed to inspect installed service '{}'. Reason: '{}'", m_ServiceName, Ec.message())); } if (Info.Status == ServiceStatus::NotInstalled) { - ZEN_CONSOLE("Service '{}' is not installed", m_ServiceName); - return 1; + throw std::runtime_error(fmt::format("Service '{}' is not installed", m_ServiceName)); } if (Info.Status != ServiceStatus::Stopped) { ZEN_CONSOLE("Service '{}' is already running", m_ServiceName); - return 1; + return; } if (!IsElevated()) { - return RunElevated(m_AllowElevation); + RunElevated(m_AllowElevation); + return; } Ec = StartService(m_ServiceName); if (Ec) { - ZEN_CONSOLE("Failed to start service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); - return 1; + throw std::runtime_error(fmt::format("Failed to start service '{}'. Reason: '{}'", m_ServiceName, Ec.message())); } ZEN_CONSOLE("Started service '{}' successfully", m_ServiceName); } @@ -577,35 +557,31 @@ ServiceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) std::error_code Ec = QueryInstalledService(m_ServiceName, Info); if (Ec) { - ZEN_CONSOLE("Failed to inspect installed service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); - return 1; + throw std::runtime_error(fmt::format("Failed to inspect installed service '{}'. Reason: '{}'", m_ServiceName, Ec.message())); } if (Info.Status == ServiceStatus::NotInstalled) { - ZEN_CONSOLE("Service '{}' is not installed", m_ServiceName); - return 1; + throw std::runtime_error(fmt::format("Service '{}' is not installed", m_ServiceName)); } if (Info.Status != ServiceStatus::Running) { ZEN_CONSOLE("Service '{}' is not running", m_ServiceName); - return 1; + return; } if (!IsElevated()) { - return RunElevated(m_AllowElevation); + RunElevated(m_AllowElevation); + return; } Ec = StopService(m_ServiceName); if (Ec) { - ZEN_CONSOLE("Failed to stop service '{}'. Reason: '{}'", m_ServiceName, Ec.message()); - return 1; + throw std::runtime_error(fmt::format("Failed to stop service '{}'. Reason: '{}'", m_ServiceName, Ec.message())); } ZEN_CONSOLE("Stopped service '{}' successfully", m_ServiceName); } - - return 0; } } // namespace zen diff --git a/src/zen/cmds/service_cmd.h b/src/zen/cmds/service_cmd.h index 359e8e854..ee98620d1 100644 --- a/src/zen/cmds/service_cmd.h +++ b/src/zen/cmds/service_cmd.h @@ -17,7 +17,7 @@ public: ServiceCommand(); ~ServiceCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: diff --git a/src/zen/cmds/status_cmd.cpp b/src/zen/cmds/status_cmd.cpp index 5b172f1a5..c43f85052 100644 --- a/src/zen/cmds/status_cmd.cpp +++ b/src/zen/cmds/status_cmd.cpp @@ -20,14 +20,14 @@ StatusCommand::StatusCommand() StatusCommand::~StatusCommand() = default; -int +void StatusCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } uint16_t EffectivePort = 0; @@ -35,8 +35,7 @@ StatusCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { if (!IsFile(m_DataDir / ".lock")) { - ZEN_CONSOLE_ERROR("Lock file does not exist in directory '{}'", m_DataDir); - return 1; + throw std::runtime_error(fmt::format("Lock file does not exist in directory '{}'", m_DataDir)); } CbValidateError ValidateResult = CbValidateError::None; if (CbObject LockFileObject = @@ -47,15 +46,14 @@ StatusCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) std::string Reason; if (!ValidateLockFileInfo(Info, Reason)) { - ZEN_CONSOLE_ERROR("Lock file in directory '{}' is not valid. Reason: '{}'", m_DataDir, Reason); - return 1; + throw std::runtime_error(fmt::format("Lock file in directory '{}' is not valid. Reason: '{}'", m_DataDir, Reason)); } EffectivePort = Info.EffectiveListenPort; } else { - ZEN_CONSOLE_ERROR("Lock file in directory '{}' is not valid. Reason: '{}'", m_DataDir, ToString(ValidateResult)); - return 1; + throw std::runtime_error( + fmt::format("Lock file in directory '{}' is malformed. Reason: '{}'", m_DataDir, ToString(ValidateResult))); } } @@ -63,7 +61,7 @@ StatusCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (!State.InitializeReadOnly()) { ZEN_CONSOLE("No Zen state found"); - return 0; + return; } ZEN_CONSOLE("{:>5} {:>6} {:>24}", "port", "pid", "session"); @@ -78,8 +76,6 @@ StatusCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ZEN_CONSOLE("{:>5} {:>6} {:>24}", Entry.EffectiveListenPort.load(), Entry.Pid.load(), SessionStringBuilder); } }); - - return 0; } } // namespace zen diff --git a/src/zen/cmds/status_cmd.h b/src/zen/cmds/status_cmd.h index 46bda9ee6..dc103a196 100644 --- a/src/zen/cmds/status_cmd.h +++ b/src/zen/cmds/status_cmd.h @@ -14,7 +14,7 @@ public: StatusCommand(); ~StatusCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: diff --git a/src/zen/cmds/top_cmd.cpp b/src/zen/cmds/top_cmd.cpp index 9794dc1c0..0e44dbbec 100644 --- a/src/zen/cmds/top_cmd.cpp +++ b/src/zen/cmds/top_cmd.cpp @@ -19,7 +19,7 @@ TopCommand::TopCommand() TopCommand::~TopCommand() = default; -int +void TopCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions, argc, argv); @@ -29,7 +29,7 @@ TopCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_CONSOLE("No Zen state found"); - return 0; + return; } int n = 0; @@ -55,8 +55,6 @@ TopCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) State.Sweep(); } } - - return 0; } ////////////////////////////////////////////////////////////////////////// @@ -67,7 +65,7 @@ PsCommand::PsCommand() PsCommand::~PsCommand() = default; -int +void PsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions, argc, argv); @@ -77,14 +75,12 @@ PsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_CONSOLE("No Zen state found"); - return 0; + return; } State.Snapshot([&](const ZenServerState::ZenServerEntry& Entry) { ZEN_CONSOLE("Port {} : pid {}", Entry.EffectiveListenPort.load(), Entry.Pid.load()); }); - - return 0; } } // namespace zen diff --git a/src/zen/cmds/top_cmd.h b/src/zen/cmds/top_cmd.h index 83410587b..74167ecfd 100644 --- a/src/zen/cmds/top_cmd.h +++ b/src/zen/cmds/top_cmd.h @@ -12,7 +12,7 @@ public: TopCommand(); ~TopCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -25,7 +25,7 @@ public: PsCommand(); ~PsCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: diff --git a/src/zen/cmds/trace_cmd.cpp b/src/zen/cmds/trace_cmd.cpp index 85caf33b8..41a30068c 100644 --- a/src/zen/cmds/trace_cmd.cpp +++ b/src/zen/cmds/trace_cmd.cpp @@ -20,21 +20,21 @@ TraceCommand::TraceCommand() TraceCommand::~TraceCommand() = default; -int +void TraceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) { - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); } zen::HttpClient Http(m_HostName); @@ -44,13 +44,12 @@ TraceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (zen::HttpClient::Response Response = Http.Post("/admin/trace/stop"sv)) { ZEN_CONSOLE("OK: {}", Response.ToText()); - return 0; } else { - ZEN_CONSOLE_ERROR("trace stop failed: {}", Response.ToText()); - return 1; + Response.ThrowError("Trace stop failed"); } + return; } std::string StartArg; @@ -68,26 +67,23 @@ TraceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (zen::HttpClient::Response Response = Http.Post(fmt::format("/admin/trace/start?{}"sv, StartArg))) { ZEN_CONSOLE("OK: {}", Response.ToText()); - return 0; } else { - ZEN_CONSOLE_ERROR("trace start failed: {}", Response.ToText()); - return 1; + Response.ThrowError("Trace start failed"); } } - - if (zen::HttpClient::Response Response = Http.Get("/admin/trace"sv)) - { - ZEN_CONSOLE("OK: {}", Response.ToText()); - return 0; - } else { - ZEN_CONSOLE_ERROR("trace status failed: {}", Response.ToText()); + if (zen::HttpClient::Response Response = Http.Get("/admin/trace"sv)) + { + ZEN_CONSOLE("OK: {}", Response.ToText()); + } + else + { + Response.ThrowError("Trace status failed"); + } } - - return 1; } } // namespace zen diff --git a/src/zen/cmds/trace_cmd.h b/src/zen/cmds/trace_cmd.h index 404fb57a4..a6c9742b7 100644 --- a/src/zen/cmds/trace_cmd.h +++ b/src/zen/cmds/trace_cmd.h @@ -14,7 +14,7 @@ public: TraceCommand(); ~TraceCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: diff --git a/src/zen/cmds/up_cmd.cpp b/src/zen/cmds/up_cmd.cpp index 8deb6c70e..a219677df 100644 --- a/src/zen/cmds/up_cmd.cpp +++ b/src/zen/cmds/up_cmd.cpp @@ -31,7 +31,7 @@ UpCommand::UpCommand() UpCommand::~UpCommand() = default; -int +void UpCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { using namespace std::literals; @@ -40,12 +40,12 @@ UpCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (!ParseOptions(argc, argv)) { - return 0; + return; } if (m_ShowConsole && m_ShowLog) { - throw OptionParseException("--show-console can not be used in combination with --show-log"); + throw OptionParseException("'--show-console' conficts with '--show-log'", m_Options.help()); } { @@ -73,7 +73,7 @@ UpCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) RunningEntries[0].DesiredPort, RunningEntries[0].EffectivePort, RunningEntries[0].Pid); - return 0; + return; } } } @@ -99,20 +99,25 @@ UpCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { if (Server.IsRunning()) { - ZEN_CONSOLE_ERROR("Zen server launch failed (timed out), terminating"); + ZEN_CONSOLE_WARN("Zen server launch failed (timed out), terminating"); Server.Terminate(); if (!m_ShowConsole) { ZEN_CONSOLE("{}", Server.GetLogOutput()); } - return 111; + throw std::runtime_error("Zen server launch failed (timed out), launched process was terminated"); } - int ReturnCode = Server.Shutdown(); + int ServerExitCode = Server.Shutdown(); if (!m_ShowConsole) { ZEN_CONSOLE("{}", Server.GetLogOutput()); } - return ReturnCode; + if (ServerExitCode != 0) + { + throw ErrorWithReturnCode( + fmt::format("Zen server failed to get to a ready state and exited with return code {}", ServerExitCode), + ServerExitCode); + } } else { @@ -125,7 +130,6 @@ UpCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ZEN_CONSOLE("Zen server up"); } } - return 0; } ////////////////////////////////////////////////////////////////////////// @@ -139,14 +143,14 @@ AttachCommand::AttachCommand() AttachCommand::~AttachCommand() = default; -int +void AttachCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } ZenServerState Instance; @@ -158,8 +162,7 @@ AttachCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { if (!IsFile(m_DataDir / ".lock")) { - ZEN_CONSOLE("Lock file does not exist in directory '{}'", m_DataDir); - return 1; + throw std::runtime_error(fmt::format("Lock file does not exist in directory '{}'", m_DataDir)); } CbValidateError ValidateResult = CbValidateError::None; if (CbObject LockFileObject = @@ -170,33 +173,29 @@ AttachCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) std::string Reason; if (!ValidateLockFileInfo(Info, Reason)) { - ZEN_CONSOLE_ERROR("Lock file in directory '{}' is not valid. Reason: '{}'", m_DataDir, Reason); - return 1; + throw std::runtime_error(fmt::format("Lock file in directory '{}' is not valid. Reason: '{}'", m_DataDir, Reason)); } Entry = Instance.LookupByEffectivePort(Info.EffectiveListenPort); } else { - ZEN_CONSOLE_ERROR("Lock file in directory '{}' is not valid. Reason: '{}'", m_DataDir, ToString(ValidateResult)); - return 1; + throw std::runtime_error( + fmt::format("Lock file in directory '{}' is malformed. Reason: '{}'", m_DataDir, ToString(ValidateResult))); } } if (!Entry) { - ZEN_CONSOLE_ERROR("No zen server instance to add sponsor process to"); - return 1; + throw std::runtime_error("No zen server instance to add sponsor process to"); } // Sponsor processes are checked every second, so 2 second wait time should be enough if (!Entry->AddSponsorProcess(m_OwnerPid, 2000)) { - ZEN_CONSOLE_ERROR("Unable to add sponsor process to running zen server instance"); - return 1; + throw std::runtime_error("Unable to add sponsor process to running zen server instance"); } ZEN_CONSOLE("Added sponsor process {} to running instance {} on port {}", m_OwnerPid, Entry->Pid.load(), m_Port); - return 0; } ////////////////////////////////////////////////////////////////////////// @@ -211,14 +210,14 @@ DownCommand::DownCommand() DownCommand::~DownCommand() = default; -int +void DownCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } // Discover executing instances @@ -236,8 +235,7 @@ DownCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { if (!IsFile(m_DataDir / ".lock")) { - ZEN_CONSOLE_ERROR("Lock file does not exist in directory '{}'", m_DataDir); - return 1; + throw std::runtime_error(fmt::format("Lock file does not exist in directory '{}'", m_DataDir)); } CbValidateError ValidateResult = CbValidateError::None; if (CbObject LockFileObject = @@ -248,15 +246,14 @@ DownCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) std::string Reason; if (!ValidateLockFileInfo(Info, Reason)) { - ZEN_CONSOLE_ERROR("Lock file in directory '{}' is not valid. Reason: '{}'", m_DataDir, Reason); - return 1; + throw std::runtime_error(fmt::format("Lock file in directory '{}' is not valid. Reason: '{}'", m_DataDir, Reason)); } Entry = Instance.LookupByEffectivePort(Info.EffectiveListenPort); } else { - ZEN_CONSOLE_ERROR("Lock file in directory '{}' is not valid. Reason: '{}'", m_DataDir, ToString(ValidateResult)); - return 1; + throw std::runtime_error( + fmt::format("Lock file in directory '{}' is malformed. Reason: '{}'", m_DataDir, ToString(ValidateResult))); } } @@ -282,7 +279,7 @@ DownCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (Server.WaitUntilExited(100, Ec) && !Ec) { ZEN_CONSOLE("shutdown complete"); - return 0; + return; } else if (Ec) { @@ -318,12 +315,12 @@ DownCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (Entry == nullptr) { ZEN_CONSOLE("Shutdown complete"); - return 0; + return; } if (Entry->Pid.load() != ServerProcessPid) { ZEN_CONSOLE("Shutdown complete"); - return 0; + return; } Sleep(100); } @@ -337,21 +334,12 @@ DownCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (std::error_code Ec = FindProcess(ServerExePath, RunningProcess); !Ec, /*IncludeSelf*/ false) { ZEN_CONSOLE_WARN("Attempting hard terminate of zen process with pid ({})", RunningProcess.Pid()); - try - { - if (RunningProcess.Terminate(0)) - { - ZEN_CONSOLE("Terminate complete"); - return 0; - } - ZEN_CONSOLE_ERROR("Failed to terminate server, still running"); - return 1; - } - catch (const std::exception& Ex) + if (RunningProcess.Terminate(0)) { - ZEN_CONSOLE_ERROR("Failed to terminate server: '{}'", Ex.what()); - return 1; + ZEN_CONSOLE("Terminate complete"); + return; } + throw std::runtime_error("Failed to terminate server, still running"); } else { @@ -360,13 +348,11 @@ DownCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } else if (Entry) { - ZEN_CONSOLE_ERROR("Failed to shutdown of server on port {}, use --force to hard terminate process", - Entry->DesiredListenPort.load()); - return 1; + throw std::runtime_error( + fmt::format("Failed to shutdown of server on port {}, use --force to hard terminate process", Entry->DesiredListenPort.load())); } ZEN_CONSOLE("No zen server to bring down"); - return 0; } } // namespace zen diff --git a/src/zen/cmds/up_cmd.h b/src/zen/cmds/up_cmd.h index c9af16749..2e822d5fc 100644 --- a/src/zen/cmds/up_cmd.h +++ b/src/zen/cmds/up_cmd.h @@ -14,7 +14,7 @@ public: UpCommand(); ~UpCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -31,7 +31,7 @@ public: AttachCommand(); ~AttachCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -47,7 +47,7 @@ public: DownCommand(); ~DownCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: diff --git a/src/zen/cmds/version_cmd.cpp b/src/zen/cmds/version_cmd.cpp index 1be02331d..979278e6c 100644 --- a/src/zen/cmds/version_cmd.cpp +++ b/src/zen/cmds/version_cmd.cpp @@ -32,13 +32,13 @@ VersionCommand::VersionCommand() VersionCommand::~VersionCommand() = default; -int +void VersionCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); if (!ParseOptions(argc, argv)) { - return 0; + return; } std::string Version; @@ -69,13 +69,14 @@ VersionCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) Parameters.Entries.insert_or_assign("detailed", "true"); } const std::string_view VersionRequest("/health/version"sv); - HttpClient::Response Response = Client.Get(VersionRequest, {}, Parameters); - if (!Response.IsSuccess()) + if (HttpClient::Response Response = Client.Get(VersionRequest, {}, Parameters)) { - ZEN_CONSOLE_ERROR("{} failed: {}", VersionRequest, Response.ErrorMessage(""sv)); - return 1; + Version = Response.AsText(); + } + else + { + Response.ThrowError(fmt::format("{} failed", VersionRequest)); } - Version = Response.AsText(); } if (m_OutputPath.empty()) @@ -90,7 +91,6 @@ VersionCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) OutputFile.Write(Version.data(), Version.length(), 0); OutputFile.Close(); } - - return 0; } + } // namespace zen diff --git a/src/zen/cmds/version_cmd.h b/src/zen/cmds/version_cmd.h index 7a910e463..934ede868 100644 --- a/src/zen/cmds/version_cmd.h +++ b/src/zen/cmds/version_cmd.h @@ -15,7 +15,7 @@ public: VersionCommand(); ~VersionCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: diff --git a/src/zen/cmds/vfs_cmd.cpp b/src/zen/cmds/vfs_cmd.cpp index e8802d990..79ec20ce2 100644 --- a/src/zen/cmds/vfs_cmd.cpp +++ b/src/zen/cmds/vfs_cmd.cpp @@ -28,14 +28,14 @@ VfsCommand::~VfsCommand() { } -int +void VfsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions, argc, argv); - if (!ZenCmdBase::ParseOptions(argc, argv)) + if (!ParseOptions(argc, argv)) { - return 0; + return; } // Validate arguments @@ -43,14 +43,14 @@ VfsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) m_HostName = ResolveTargetHostSpec(m_HostName); if (m_HostName.empty()) - throw OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", m_Options.help()); HttpClient Http(m_HostName); if (m_Verb == "mount"sv) { if (m_MountPath.empty()) - throw OptionParseException("No source specified"); + throw OptionParseException("'--vfs-path' is required", m_Options.help()); CbObjectWriter Cbo; Cbo << "method" @@ -66,8 +66,6 @@ VfsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) else { Result.ThrowError("VFS mount request failed"sv); - - return 1; } } else if (m_Verb == "unmount"sv) @@ -82,8 +80,6 @@ VfsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) else { Result.ThrowError("VFS unmount request failed"sv); - - return 1; } } else if (m_Verb == "info"sv) @@ -97,12 +93,8 @@ VfsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) else { Result.ThrowError("VFS info fetch failed"sv); - - return 1; } } - - return 0; } } // namespace zen diff --git a/src/zen/cmds/vfs_cmd.h b/src/zen/cmds/vfs_cmd.h index 9b2497c0e..5deaa02fa 100644 --- a/src/zen/cmds/vfs_cmd.h +++ b/src/zen/cmds/vfs_cmd.h @@ -12,7 +12,7 @@ public: VfsCommand(); ~VfsCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: diff --git a/src/zen/cmds/wipe_cmd.cpp b/src/zen/cmds/wipe_cmd.cpp index 5a9d0174e..d2bc2f5c4 100644 --- a/src/zen/cmds/wipe_cmd.cpp +++ b/src/zen/cmds/wipe_cmd.cpp @@ -543,7 +543,7 @@ WipeCommand::WipeCommand() WipeCommand::~WipeCommand() = default; -int +void WipeCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); @@ -553,9 +553,9 @@ WipeCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) signal(SIGBREAK, SignalCallbackHandler); #endif // ZEN_PLATFORM_WINDOWS - if (!ZenCmdBase::ParseOptions(argc, argv)) + if (!ParseOptions(argc, argv)) { - return 0; + return; } Quiet = m_Quiet; @@ -567,7 +567,7 @@ WipeCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (!IsDir(m_Directory)) { - return 0; + return; } while (!m_Yes) @@ -583,13 +583,11 @@ WipeCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) } else if (Reponse == "n" || Reponse == "no") { - return 0; + return; } } CleanDirectory(m_Directory, {}, !m_KeepReadOnlyFiles, m_Dryrun); - - return 0; } } // namespace zen diff --git a/src/zen/cmds/wipe_cmd.h b/src/zen/cmds/wipe_cmd.h index 0e910bb81..d0693a757 100644 --- a/src/zen/cmds/wipe_cmd.h +++ b/src/zen/cmds/wipe_cmd.h @@ -18,7 +18,7 @@ public: ~WipeCommand(); virtual cxxopts::Options& Options() override { return m_Options; } - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual ZenCmdCategory& CommandCategory() const override { return g_UtilitiesCategory; } private: diff --git a/src/zen/cmds/workspaces_cmd.cpp b/src/zen/cmds/workspaces_cmd.cpp index 3930e56b7..9bb118eac 100644 --- a/src/zen/cmds/workspaces_cmd.cpp +++ b/src/zen/cmds/workspaces_cmd.cpp @@ -117,7 +117,7 @@ WorkspaceCommand::WorkspaceCommand() WorkspaceCommand::~WorkspaceCommand() = default; -int +void WorkspaceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); @@ -129,12 +129,12 @@ WorkspaceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) int ParentCommandArgCount = GetSubCommand(m_Options, argc, argv, m_SubCommands, SubOption, SubCommandArguments); if (!ParseOptions(ParentCommandArgCount, argv)) { - return 0; + return; } if (SubOption == nullptr) { - throw zen::OptionParseException("command verb is missing"); + throw OptionParseException("'verb' option is required", m_Options.help()); } m_HostName = ResolveTargetHostSpec(m_HostName); @@ -144,7 +144,7 @@ WorkspaceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) m_SystemRootDir = PickDefaultSystemRootDirectory(); if (m_SystemRootDir.empty()) { - throw zen::OptionParseException("unable to resolve system root directory"); + throw std::runtime_error("Unable to resolve default system root directory"); } } @@ -152,14 +152,14 @@ WorkspaceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (!ParseOptions(*SubOption, gsl::narrow<int>(SubCommandArguments.size()), SubCommandArguments.data())) { - return 0; + return; } if (SubOption == &m_CreateOptions) { if (m_Path.empty()) { - throw zen::OptionParseException(fmt::format("path is required\n{}", m_CreateOptions.help())); + throw OptionParseException("'--root-path' is required", SubOption->help()); } std::filesystem::path Path = StringToPath(m_Path); @@ -167,12 +167,12 @@ WorkspaceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) if (m_Id.empty()) { m_Id = Workspaces::PathToId(Path).ToString(); - ZEN_CONSOLE("Using generated workspace id {} from path '{}'", m_Id, Path); + ZEN_CONSOLE("Using generated workspace id '{}' from '--root-path' '{}'", m_Id, Path); } if (Oid::TryFromHexString(m_Id) == Oid::Zero) { - throw zen::OptionParseException(fmt::format("id '{}' is invalid", m_Id)); + throw OptionParseException(fmt::format("'--workspace' ('{}') is malformed", m_Id), SubOption->help()); } if (Workspaces::AddWorkspace( @@ -185,16 +185,16 @@ WorkspaceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) HttpClient Http(m_HostName); if (HttpClient::Response Result = Http.Get("/ws/refresh"); !Result) { - ZEN_CONSOLE_ERROR("Failed to refresh workspaces for host {}. Reason: '{}'", m_HostName, Result.ErrorMessage(""sv)); + ZEN_CONSOLE_WARN("Failed to refresh workspaces for host {}. Reason: '{}'", m_HostName, Result.ErrorMessage(""sv)); } } ZEN_CONSOLE("Added/updated workspace {}", m_Id); - return 0; + return; } else { ZEN_CONSOLE_WARN("Workspace {} already exists", m_Id); - return 0; + return; } } @@ -206,7 +206,7 @@ WorkspaceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) static std::vector<Workspaces::WorkspaceConfiguration> Configs = Workspaces::ReadConfig(Log(), StatePath, Error); if (!Error.empty()) { - ZEN_CONSOLE_ERROR("Failed to read workspaces state from '{}'. Reason: '{}'", StatePath, Error); + throw std::runtime_error(fmt::format("Failed to read workspaces state from '{}'. Reason: '{}'", StatePath, Error)); } else { @@ -216,20 +216,20 @@ WorkspaceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) ShowWorkspace(Config, " "sv); } } - return 0; + return; } else { if (Oid::TryFromHexString(m_Id) == Oid::Zero) { - throw zen::OptionParseException(fmt::format("id '{}' is invalid", m_Id)); + throw OptionParseException(fmt::format("'--workspace' ('{}') is malformed", m_Id), SubOption->help()); } Workspaces::WorkspaceConfiguration Workspace = Workspaces::FindWorkspace(Log(), StatePath, Oid::FromHexString(m_Id)); if (Workspace.Id != Oid::Zero) { ShowWorkspace(Workspace, ""sv); - return 0; + return; } else { @@ -242,12 +242,12 @@ WorkspaceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { if (m_Id.empty()) { - throw zen::OptionParseException(fmt::format("id is required", m_RemoveOptions.help())); + throw OptionParseException("'--workspace' is required", SubOption->help()); } if (Oid::TryFromHexString(m_Id) == Oid::Zero) { - throw zen::OptionParseException(fmt::format("id '{}' is invalid", m_Id)); + throw OptionParseException(fmt::format("'--workspace' ('{}') is malformed", m_Id), SubOption->help()); } if (Workspaces::RemoveWorkspace(Log(), StatePath, Oid::FromHexString(m_Id))) @@ -257,7 +257,7 @@ WorkspaceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) HttpClient Http(m_HostName); if (HttpClient::Response Result = Http.Get("/ws/refresh"); !Result) { - ZEN_CONSOLE_ERROR("Failed to refresh workspaces for host {}. Reason: '{}'", m_HostName, Result.ErrorMessage(""sv)); + ZEN_CONSOLE_WARN("Failed to refresh workspaces for host {}. Reason: '{}'", m_HostName, Result.ErrorMessage(""sv)); } } ZEN_CONSOLE("Removed workspace {}", m_Id); @@ -266,7 +266,7 @@ WorkspaceCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_CONSOLE_WARN("Workspace {} does not exist", m_Id); } - return 0; + return; } ZEN_ASSERT(false); @@ -368,7 +368,7 @@ WorkspaceShareCommand::WorkspaceShareCommand() WorkspaceShareCommand::~WorkspaceShareCommand() = default; -int +void WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); @@ -380,12 +380,12 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** int ParentCommandArgCount = GetSubCommand(m_Options, argc, argv, m_SubCommands, SubOption, SubCommandArguments); if (!ParseOptions(ParentCommandArgCount, argv)) { - return 0; + return; } if (SubOption == nullptr) { - throw zen::OptionParseException("command verb is missing"); + throw OptionParseException("'verb' option is required", m_Options.help()); } m_HostName = ResolveTargetHostSpec(m_HostName); @@ -395,7 +395,7 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** m_SystemRootDir = PickDefaultSystemRootDirectory(); if (m_SystemRootDir.empty()) { - throw zen::OptionParseException("unable to resolve system root directory"); + throw std::runtime_error("Unable to resolve default system root directory"); } } else @@ -407,7 +407,7 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** if (!ParseOptions(*SubOption, gsl::narrow<int>(SubCommandArguments.size()), SubCommandArguments.data())) { - return 0; + return; } if (SubOption == &m_CreateOptions) @@ -416,20 +416,19 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** { if (m_WorkspaceId.empty()) { - throw zen::OptionParseException("workspace id or root path is required"); + throw OptionParseException("'--workspace' or '--root-path' is required", SubOption->help()); } Oid WorkspaceId = Oid::TryFromHexString(m_WorkspaceId); if (WorkspaceId == Oid::Zero) { - throw zen::OptionParseException(fmt::format("id '{}' is invalid", m_WorkspaceId)); + throw OptionParseException(fmt::format("'--workspace' ('{}') is malformed", m_WorkspaceId), SubOption->help()); } Workspaces::WorkspaceConfiguration WorkspaceConfig = Workspaces::FindWorkspace(Log(), StatePath, WorkspaceId); if (WorkspaceConfig.Id == Oid::Zero) { - ZEN_CONSOLE_ERROR("Workspace {} does not exist", m_WorkspaceId); - return 0; + throw std::runtime_error(fmt::format("Workspace {} does not exist", m_WorkspaceId)); } m_WorkspaceRoot = WorkspaceConfig.RootPath; } @@ -445,7 +444,7 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** { if (Oid::TryFromHexString(m_WorkspaceId) == Oid::Zero) { - throw zen::OptionParseException(fmt::format("workspace id '{}' is invalid", m_WorkspaceId)); + throw OptionParseException(fmt::format("'--workspace' ('{}') is malformed", m_WorkspaceId), SubOption->help()); } } if (Workspaces::AddWorkspace(Log(), StatePath, {.Id = Oid::FromHexString(m_WorkspaceId), .RootPath = m_WorkspaceRoot})) @@ -469,7 +468,7 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** if (Oid::TryFromHexString(m_ShareId) == Oid::Zero) { - throw zen::OptionParseException(fmt::format("workspace id '{}' is invalid", m_ShareId)); + throw OptionParseException(fmt::format("'--workspace' ('{}') is malformed", m_ShareId), SubOption->help()); } if (Workspaces::AddWorkspaceShare(Log(), @@ -481,16 +480,14 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** HttpClient Http(m_HostName); if (HttpClient::Response Result = Http.Get("/ws/refresh"); !Result) { - ZEN_CONSOLE_ERROR("Failed to refresh workspaces for host {}. Reason: '{}'", m_HostName, Result.ErrorMessage(""sv)); + ZEN_CONSOLE_WARN("Failed to refresh workspaces for host {}. Reason: '{}'", m_HostName, Result.ErrorMessage(""sv)); } } ZEN_CONSOLE("Created workspace share {}", m_ShareId); - return 0; } else { ZEN_CONSOLE("Workspace share {} already exists", m_ShareId); - return 0; } } @@ -503,50 +500,47 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** Workspaces::FindWorkspaceShare(Log(), StatePath, m_Alias, WorkspaceConfig); if (ShareConfig.Id == Oid::Zero) { - ZEN_CONSOLE("Workspace share with alias {} does not exist", m_Alias); - return 0; + throw std::runtime_error(fmt::format("Workspace share with alias {} does not exist", m_Alias)); } ShowShare(ShareConfig, WorkspaceConfig.Id, ""sv); - return 0; + return; } if (m_WorkspaceId.empty()) { - throw zen::OptionParseException("workspace id or root path is required"); + throw OptionParseException("'--workspace' or '--root-path' is required", SubOption->help()); } if (Oid::TryFromHexString(m_WorkspaceId) == Oid::Zero) { - throw zen::OptionParseException(fmt::format("workspace id '{}' is invalid", m_WorkspaceId)); + throw OptionParseException(fmt::format("'--workspace' ('{}') is malformed", m_WorkspaceId), SubOption->help()); } Workspaces::WorkspaceConfiguration WorkspaceConfig = Workspaces::FindWorkspace(Log(), StatePath, Oid::FromHexString(m_WorkspaceId)); if (WorkspaceConfig.Id == Oid::Zero) { - ZEN_CONSOLE("Workspace {} does not exist", m_WorkspaceId); - return 0; + throw std::runtime_error(fmt::format("Workspace {} does not exist", m_WorkspaceId)); } std::filesystem::path WorkspaceRoot = WorkspaceConfig.RootPath; if (m_ShareId.empty()) { - throw zen::OptionParseException("share id is required"); + throw OptionParseException("'--share' is required", SubOption->help()); } if (Oid::TryFromHexString(m_ShareId) == Oid::Zero) { - throw zen::OptionParseException(fmt::format("workspace id '{}' is invalid", m_ShareId)); + throw OptionParseException(fmt::format("'--share' ('{}') is malformed", m_ShareId), SubOption->help()); } Workspaces::WorkspaceShareConfiguration Share = Workspaces::FindWorkspaceShare(Log(), WorkspaceRoot, Oid::FromHexString(m_ShareId)); if (Share.Id == Oid::Zero) { - ZEN_CONSOLE("Workspace share {} does not exist", m_ShareId); - return 0; + throw std::runtime_error(fmt::format("Workspace share {} does not exist", m_ShareId)); } ShowShare(Share, Oid::Zero, ""sv); - return 0; + return; } if (SubOption == &m_RemoveOptions) @@ -560,7 +554,7 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** if (ShareConfig.Id == Oid::Zero) { ZEN_CONSOLE("Workspace share with alias {} does not exist", m_Alias); - return 0; + return; } m_ShareId = ShareConfig.Id.ToString(); m_WorkspaceId = WorkspaceConfig.Id.ToString(); @@ -569,30 +563,30 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** if (m_WorkspaceId.empty()) { - throw zen::OptionParseException("workspace id or root path is required"); + throw OptionParseException("'--workspace' or '--root-path' is required", SubOption->help()); } if (Oid::TryFromHexString(m_WorkspaceId) == Oid::Zero) { - throw zen::OptionParseException(fmt::format("workspace id '{}' is invalid", m_WorkspaceId)); + throw OptionParseException(fmt::format("'--workspace' ('{}') is malformed", m_WorkspaceId), SubOption->help()); } Workspaces::WorkspaceConfiguration WorkspaceConfig = Workspaces::FindWorkspace(Log(), StatePath, Oid::FromHexString(m_WorkspaceId)); if (WorkspaceConfig.Id == Oid::Zero) { ZEN_CONSOLE("Workspace {} does not exist", m_WorkspaceId); - return 0; + return; } WorkspaceRoot = WorkspaceConfig.RootPath; if (m_ShareId.empty()) { - throw zen::OptionParseException("share id is required"); + throw OptionParseException("'--share' is required", SubOption->help()); } if (Oid::TryFromHexString(m_ShareId) == Oid::Zero) { - throw zen::OptionParseException(fmt::format("workspace id '{}' is invalid", m_ShareId)); + throw OptionParseException(fmt::format("'--share' ('{}') is malformed", m_ShareId), SubOption->help()); } if (Workspaces::RemoveWorkspaceShare(Log(), WorkspaceRoot, Oid::FromHexString(m_ShareId))) @@ -602,16 +596,16 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** HttpClient Http(m_HostName); if (HttpClient::Response Result = Http.Get("/ws/refresh"); !Result) { - ZEN_CONSOLE_ERROR("Failed to refresh workspaces for host {}. Reason: '{}'", m_HostName, Result.ErrorMessage(""sv)); + ZEN_CONSOLE_WARN("Failed to refresh workspaces for host {}. Reason: '{}'", m_HostName, Result.ErrorMessage(""sv)); } } ZEN_CONSOLE("Removed workspace share {}", m_ShareId); - return 0; + return; } else { ZEN_CONSOLE("Removed workspace share {} does not exist", m_ShareId); - return 0; + return; } } @@ -620,12 +614,12 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** { if (m_WorkspaceId.empty()) { - throw zen::OptionParseException("workspace id is required"); + throw OptionParseException("'--workspace' is required", Opts.help()); } if (m_ShareId.empty()) { - throw zen::OptionParseException(fmt::format("share id is required", Opts.help())); + throw OptionParseException("'--share' is required", Opts.help()); } return fmt::format("{}/{}", m_WorkspaceId, m_ShareId); } @@ -649,20 +643,19 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** if (m_HostName.empty()) { - throw zen::OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", SubOption->help()); } HttpClient Http(m_HostName); if (HttpClient::Response Result = Http.Get(fmt::format("/ws/{}/files", GetShareIdentityUrl(m_FilesOptions)), {}, Params)) { ZEN_CONSOLE("{}: {}", Result, Result.ToText()); - return 0; } else { Result.ThrowError("failed to get workspace share files"sv); - return 1; } + return; } if (SubOption == &m_EntriesOptions) @@ -683,20 +676,19 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** if (m_HostName.empty()) { - throw zen::OptionParseException("unable to resolve server specification"); + throw OptionParseException("Unable to resolve server specification", SubOption->help()); } HttpClient Http(m_HostName); if (HttpClient::Response Result = Http.Get(fmt::format("/ws/{}/entries", GetShareIdentityUrl(m_EntriesOptions)), {}, Params)) { ZEN_CONSOLE("{}: {}", Result, Result.ToText()); - return 0; } else { Result.ThrowError("failed to get workspace share entries"sv); - return 1; } + return; } auto ChunksToOidStrings = [](HttpClient& Http, @@ -751,14 +743,14 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** if (SubOption == &m_GetChunkOptions) { - if (m_ChunkId.empty()) + if (m_HostName.empty()) { - throw zen::OptionParseException("chunk id is required"); + throw OptionParseException("Unable to resolve server specification", SubOption->help()); } - if (m_HostName.empty()) + if (m_ChunkId.empty()) { - throw zen::OptionParseException("unable to resolve server specification"); + throw OptionParseException("'--chunk' is required", SubOption->help()); } HttpClient Http(m_HostName); @@ -777,30 +769,29 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** if (HttpClient::Response Result = Http.Get(fmt::format("/ws/{}/{}", GetShareIdentityUrl(m_GetChunkOptions), m_ChunkId), {}, Params)) { ZEN_CONSOLE("{}: Bytes: {}", Result, NiceBytes(Result.ResponsePayload.GetSize())); - return 0; } else { Result.ThrowError("failed to get workspace share chunk"sv); - return 1; } + return; } if (SubOption == &m_GetChunkBatchOptions) { - if (m_ShareId.empty()) + if (m_HostName.empty()) { - throw zen::OptionParseException(fmt::format("share id is required", m_InfoOptions.help())); + throw OptionParseException("Unable to resolve server specification", SubOption->help()); } - if (m_ChunkIds.empty()) + if (m_ShareId.empty()) { - throw zen::OptionParseException("share is is required"); + throw OptionParseException("'--share' is required", SubOption->help()); } - if (m_HostName.empty()) + if (m_ChunkIds.empty()) { - throw zen::OptionParseException("unable to resolve server specification"); + throw OptionParseException("'--chunks' is required", SubOption->help()); } HttpClient Http(m_HostName); @@ -832,13 +823,12 @@ WorkspaceShareCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** { ZEN_CONSOLE("{}: Bytes: {}", m_ChunkIds[Index], NiceBytes(Results[Index].GetSize())); } - return 0; } else { Result.ThrowError("failed to get workspace share batch"sv); - return 1; } + return; } ZEN_ASSERT(false); diff --git a/src/zen/cmds/workspaces_cmd.h b/src/zen/cmds/workspaces_cmd.h index d85d8f7d8..f5880125b 100644 --- a/src/zen/cmds/workspaces_cmd.h +++ b/src/zen/cmds/workspaces_cmd.h @@ -17,7 +17,7 @@ public: WorkspaceCommand(); ~WorkspaceCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: @@ -49,7 +49,7 @@ public: WorkspaceShareCommand(); ~WorkspaceShareCommand(); - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; virtual cxxopts::Options& Options() override { return m_Options; } private: diff --git a/src/zen/zen.cpp b/src/zen/zen.cpp index 240ba2a81..db63624ce 100644 --- a/src/zen/zen.cpp +++ b/src/zen/zen.cpp @@ -72,6 +72,35 @@ ZEN_THIRD_PARTY_INCLUDES_END namespace zen { +enum class ReturnCode : std::int8_t +{ + kSuccess = 0, + + kOtherError = 1, + + kBadInput = 2, + kOutOfMemory = 16, + kOutOfDisk = 17, + kAssertError = 70, + + kHttpOtherClientError = 80, + kHttpCantConnectError = 81, // CONNECTION_FAILURE + kHttpNotFound = 66, // NotFound(404) + kHttpUnauthorized = 77, // Unauthorized(401), + kHttpSLLError = + 82, // SSL_CONNECT_ERROR, SSL_LOCAL_CERTIFICATE_ERROR, SSL_REMOTE_CERTIFICATE_ERROR, SSL_CACERT_ERROR, GENERIC_SSL_ERROR + kHttpForbidden = 83, // Forbidden(403) + kHttpTimeout = 84, // NETWORK_RECEIVE_ERROR, NETWORK_SEND_FAILURE, OPERATION_TIMEDOUT, RequestTimeout(408) + kHttpConflict = 85, // Conflict(409) + kHttpNoHost = 86, // HOST_RESOLUTION_FAILURE, PROXY_RESOLUTION_FAILURE + + kHttpOtherServerError = 90, + kHttpInternalServerError = 91, // InternalServerError(500) + kHttpServiceUnavailable = 69, // ServiceUnavailable(503) + kHttpBadGateway = 92, // BadGateway(502) + kHttpGatewayTimeout = 93, // GatewayTimeout(504) +}; + ZenCmdCategory DefaultCategory{.Name = "general commands"}; ZenCmdCategory g_UtilitiesCategory{.Name = "utility commands"}; ZenCmdCategory g_ProjectStoreCategory{.Name = "project store commands"}; @@ -101,7 +130,7 @@ ZenCmdBase::ParseOptions(cxxopts::Options& CmdOptions, int argc, char** argv) } catch (const std::exception& Ex) { - throw zen::OptionParseException(Ex.what()); + throw zen::OptionParseException(Ex.what(), CmdOptions.help()); } CmdOptions.show_positional_help(); @@ -127,7 +156,7 @@ ZenCmdBase::ParseOptions(cxxopts::Options& CmdOptions, int argc, char** argv) First = false; } - throw zen::OptionParseException(fmt::format("Invalid arguments: {}", StringBuilder.ToView())); + throw zen::OptionParseException(fmt::format("Invalid arguments: {}", StringBuilder.ToView()), {}); } return true; @@ -161,62 +190,68 @@ ZenCmdBase::GetSubCommand(cxxopts::Options&, return argc; } -std::string -ZenCmdBase::FormatHttpResponse(const cpr::Response& Response) +static ReturnCode +GetReturnCodeFromHttpResult(int Error, HttpResponseCode ResponseCode) { - if (Response.error.code != cpr::ErrorCode::OK) + if ((cpr::ErrorCode)Error != cpr::ErrorCode::OK) { - if (Response.error.message.empty()) + switch ((cpr::ErrorCode)Error) { - return fmt::format("Request '{}' failed, error code {}", Response.url.str(), static_cast<int>(Response.error.code)); + case cpr::ErrorCode::CONNECTION_FAILURE: + return ReturnCode::kHttpCantConnectError; + case cpr::ErrorCode::HOST_RESOLUTION_FAILURE: + case cpr::ErrorCode::PROXY_RESOLUTION_FAILURE: + return ReturnCode::kHttpNoHost; + case cpr::ErrorCode::INTERNAL_ERROR: + case cpr::ErrorCode::NETWORK_RECEIVE_ERROR: + case cpr::ErrorCode::NETWORK_SEND_FAILURE: + case cpr::ErrorCode::OPERATION_TIMEDOUT: + return ReturnCode::kHttpTimeout; + case cpr::ErrorCode::SSL_CONNECT_ERROR: + case cpr::ErrorCode::SSL_LOCAL_CERTIFICATE_ERROR: + case cpr::ErrorCode::SSL_REMOTE_CERTIFICATE_ERROR: + case cpr::ErrorCode::SSL_CACERT_ERROR: + case cpr::ErrorCode::GENERIC_SSL_ERROR: + return ReturnCode::kHttpSLLError; + default: + return ReturnCode::kHttpOtherClientError; } - return fmt::format("Request '{}' failed. Reason: '{}' ({})", - Response.url.str(), - Response.error.message, - static_cast<int>(Response.error.code)); } - - std::string Content; - if (auto It = Response.header.find("Content-Type"); It != Response.header.end()) - { - zen::HttpContentType ContentType = zen::ParseContentType(It->second); - if (ContentType == zen::HttpContentType::kText) - { - Content = Response.text; - } - else if (ContentType == zen::HttpContentType::kJSON) - { - Content = fmt::format("\n{}", Response.text); - } - else if (!Response.text.empty()) - { - Content = fmt::format("[{}]", MapContentTypeToString(ContentType)); - } - } - - std::string_view ResponseString = zen::ReasonStringForHttpResultCode( - Response.status_code == static_cast<long>(zen::HttpResponseCode::NoContent) ? static_cast<long>(zen::HttpResponseCode::OK) - : Response.status_code); - if (Content.empty()) - { - return std::string(ResponseString); - } - - return fmt::format("{}: {}", ResponseString, Content); -} - -int -ZenCmdBase::MapHttpToCommandReturnCode(const cpr::Response& Response) -{ - if (zen::IsHttpSuccessCode(Response.status_code)) + else if (IsHttpSuccessCode(ResponseCode)) { - return 0; + return ReturnCode::kSuccess; } - if (Response.error.code != cpr::ErrorCode::OK) + else { - return static_cast<int>(Response.error.code); + switch (ResponseCode) + { + case HttpResponseCode::Unauthorized: + return ReturnCode::kHttpUnauthorized; + case HttpResponseCode::NotFound: + return ReturnCode::kHttpNotFound; + case HttpResponseCode::Forbidden: + return ReturnCode::kHttpForbidden; + case HttpResponseCode::Conflict: + return ReturnCode::kHttpConflict; + case HttpResponseCode::InternalServerError: + return ReturnCode::kHttpInternalServerError; + case HttpResponseCode::ServiceUnavailable: + return ReturnCode::kHttpServiceUnavailable; + case HttpResponseCode::BadGateway: + return ReturnCode::kHttpBadGateway; + case HttpResponseCode::GatewayTimeout: + return ReturnCode::kHttpGatewayTimeout; + default: + if (ResponseCode >= HttpResponseCode::InternalServerError) + { + return ReturnCode::kHttpOtherServerError; + } + else + { + return ReturnCode::kHttpOtherClientError; + } + } } - return 1; } std::string @@ -945,7 +980,7 @@ main(int argc, char** argv) printf("\n"); } - exit(0); + return (int)ReturnCode::kSuccess; } #if ZEN_USE_SENTRY @@ -1022,86 +1057,107 @@ main(int argc, char** argv) { if (StrCaseCompare(SubCommand.c_str(), CmdInfo.CmdName) == 0) { - cxxopts::Options& VerbOptions = CmdInfo.Cmd->Options(); - try { - return CmdInfo.Cmd->Run(GlobalOptions, (int)CommandArgVec.size(), CommandArgVec.data()); + CmdInfo.Cmd->Run(GlobalOptions, (int)CommandArgVec.size(), CommandArgVec.data()); + return (int)ReturnCode::kSuccess; } catch (const OptionParseException& Ex) { - ZEN_CONSOLE_ERROR("Invalid arguments for command '{}': {}\n\n{}", SubCommand, Ex.what(), VerbOptions.help()); - exit(11); + ZEN_CONSOLE("{}\n", Ex.m_Help); + ZEN_CONSOLE_ERROR("Invalid arguments for command '{}': {}", SubCommand, Ex.what()); + return (int)ReturnCode::kBadInput; } catch (const std::system_error& Ex) { if (IsOOD(Ex)) { ZEN_CONSOLE_ERROR("Operation failed due to out of disk space: {}", Ex.what()); - exit(3); + return (int)ReturnCode::kOutOfDisk; } else if (IsOOM(Ex)) { ZEN_CONSOLE_ERROR("Operation failed due to out of memory: {}", Ex.what()); - exit(3); + return (int)ReturnCode::kOutOfMemory; } else { ZEN_CONSOLE_ERROR("Operation failed due to system error: {} ({})\n", Ex.what(), Ex.code() ? Ex.code().value() : 0); - exit(Ex.code() ? Ex.code().value() : 10); + return (int)ReturnCode::kOtherError; } } catch (const HttpClientError& Ex) { ZEN_CONSOLE_ERROR("Operation failed due to a http error: {}", Ex.what()); - exit(Ex.m_Error != 0 ? Ex.m_Error : (int)Ex.m_ResponseCode); + ReturnCode Result = GetReturnCodeFromHttpResult(Ex.m_Error, Ex.m_ResponseCode); + return (int)Result; + } + catch (const AssertException& Ex) + { + ZEN_CONSOLE_ERROR("Operation failed due to an assert exception: {}", Ex.FullDescription()); + return (int)ReturnCode::kAssertError; + } + catch (const ErrorWithReturnCode& Ex) + { + ZEN_CONSOLE_ERROR("{}", Ex.what()); + return Ex.m_ReturnCode; } catch (const std::exception& Ex) { - ZEN_CONSOLE_ERROR("Operation failed due to: {}\n", Ex.what()); - exit(11); + ZEN_CONSOLE_ERROR("{}\n", Ex.what()); + return (int)ReturnCode::kOtherError; } } } printf("Unknown command specified: '%s', exiting\n", SubCommand.c_str()); + return (int)ReturnCode::kBadInput; } catch (const OptionParseException& Ex) { - std::string HelpMessage = Options.help(); - - printf("Error parsing program arguments: %s\n\n%s", Ex.what(), HelpMessage.c_str()); - - return 9; + printf("%s\n\n", Ex.m_Help.c_str()); + printf("Invalid arguments arguments: %s", Ex.what()); + return (int)ReturnCode::kBadInput; } catch (const std::system_error& Ex) { if (IsOOD(Ex)) { printf("Operation failed due to out of disk space: %s", Ex.what()); - return 3; + return (int)ReturnCode::kOutOfDisk; } else if (IsOOM(Ex)) { printf("Operation failed due to out of memory: %s", Ex.what()); - return 3; + return (int)ReturnCode::kOutOfMemory; } else { printf("Operation failed due to system error: %s (%d)\n", Ex.what(), Ex.code() ? Ex.code().value() : 0); - return Ex.code() ? Ex.code().value() : 10; + return (int)ReturnCode::kOtherError; } } catch (const HttpClientError& Ex) { - printf("Operation failed due to a http error: %s", Ex.what()); - return Ex.m_Error != 0 ? Ex.m_Error : (int)Ex.m_ResponseCode; + printf("Error: Operation failed due to a http error: %s", Ex.what()); + ReturnCode Result = GetReturnCodeFromHttpResult(Ex.m_Error, Ex.m_ResponseCode); + return (int)Result; + } + catch (const AssertException& Ex) + { + printf("Error: Operation failed due to an assert exception: %s", Ex.FullDescription().c_str()); + return (int)ReturnCode::kAssertError; + } + catch (const ErrorWithReturnCode& Ex) + { + printf("Error: %s", Ex.what()); + return Ex.m_ReturnCode; } catch (const std::exception& Ex) { - printf("Operation failed due to: %s\n", Ex.what()); - return 11; + printf("Error: %s\n", Ex.what()); + return (int)ReturnCode::kOtherError; } - return 0; + return (int)ReturnCode::kSuccess; } diff --git a/src/zen/zen.h b/src/zen/zen.h index 40c745bc7..5ff7066a0 100644 --- a/src/zen/zen.h +++ b/src/zen/zen.h @@ -7,10 +7,6 @@ #include <zencore/zencore.h> #include <zenutil/commandlineoptions.h> -namespace cpr { -class Response; -} - namespace zen { struct ZenCliOptions @@ -35,13 +31,24 @@ extern ZenCmdCategory g_ProjectStoreCategory; extern ZenCmdCategory g_CacheStoreCategory; extern ZenCmdCategory g_StorageCategory; +class ErrorWithReturnCode : public std::runtime_error +{ +public: + using _Mybase = runtime_error; + ErrorWithReturnCode(const std::string& Message, int InReturnCode) : _Mybase(Message), m_ReturnCode(InReturnCode) {} + + ErrorWithReturnCode(const char* Message, int InReturnCode) : _Mybase(Message), m_ReturnCode(InReturnCode) {} + + const int m_ReturnCode = -1; +}; + /** Base class for command implementations */ class ZenCmdBase { public: - virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) = 0; + virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) = 0; virtual cxxopts::Options& Options() = 0; virtual ZenCmdCategory& CommandCategory() const; @@ -53,8 +60,6 @@ public: std::span<cxxopts::Options*> SubOptions, cxxopts::Options*& OutSubOption, std::vector<char*>& OutSubCommandArguments); - static std::string FormatHttpResponse(const cpr::Response& Response); - static int MapHttpToCommandReturnCode(const cpr::Response& Response); static std::string ResolveTargetHostSpec(const std::string& InHostSpec); static std::string ResolveTargetHostSpec(const std::string& InHostSpec, uint16_t& OutEffectivePort); }; diff --git a/src/zencore/include/zencore/except.h b/src/zencore/include/zencore/except.h index 6810e6ea9..c933adfd8 100644 --- a/src/zencore/include/zencore/except.h +++ b/src/zencore/include/zencore/except.h @@ -63,7 +63,9 @@ MakeErrorCodeFromLastError() noexcept class OptionParseException : public std::runtime_error { public: - inline explicit OptionParseException(const std::string& Message) : std::runtime_error(Message) {} + // inline explicit OptionParseException(const std::string& Message) : std::runtime_error(Message) {} + inline OptionParseException(const std::string& Message, const std::string& Help) : std::runtime_error(Message), m_Help(Help) {} + const std::string m_Help; }; bool IsOOM(const std::system_error& SystemError); diff --git a/src/zencore/include/zencore/uid.h b/src/zencore/include/zencore/uid.h index 64d3b6b9b..0c1079444 100644 --- a/src/zencore/include/zencore/uid.h +++ b/src/zencore/include/zencore/uid.h @@ -55,8 +55,8 @@ class StringBuilderBase; struct Oid { - static const int StringLength = 24; - typedef char String_t[StringLength + 1]; + static constexpr int StringLength = 24; + typedef char String_t[StringLength + 1]; static void Initialize(); [[nodiscard]] static Oid NewOid(); diff --git a/src/zenserver/config.cpp b/src/zenserver/config.cpp index 0397677b9..0cf5a9ca3 100644 --- a/src/zenserver/config.cpp +++ b/src/zenserver/config.cpp @@ -99,35 +99,37 @@ ValidateOptions(ZenServerOptions& ServerOptions) { if (ServerOptions.EncryptionKey.empty() == false) { - const auto Key = zen::AesKey256Bit::FromString(ServerOptions.EncryptionKey); + const auto Key = AesKey256Bit::FromString(ServerOptions.EncryptionKey); if (Key.IsValid() == false) { - throw zen::OptionParseException("Invalid AES encryption key"); + throw OptionParseException(fmt::format("'--encryption-aes-key' ('{}') is malformed", ServerOptions.EncryptionKey), {}); } } if (ServerOptions.EncryptionIV.empty() == false) { - const auto IV = zen::AesIV128Bit::FromString(ServerOptions.EncryptionIV); + const auto IV = AesIV128Bit::FromString(ServerOptions.EncryptionIV); if (IV.IsValid() == false) { - throw zen::OptionParseException("Invalid AES initialization vector"); + throw OptionParseException(fmt::format("'--encryption-aes-iv' ('{}') is malformed", ServerOptions.EncryptionIV), {}); } } if (ServerOptions.HttpServerConfig.ForceLoopback && ServerOptions.IsDedicated) { - throw zen::OptionParseException("Dedicated server can not be used with forced local server address"); + throw OptionParseException("'--dedicated' conflicts with '--http-forceloopback'", {}); } if (ServerOptions.GcConfig.AttachmentPassCount > ZenGcConfig::GcMaxAttachmentPassCount) { - throw zen::OptionParseException( - fmt::format("GC attachment pass count can not be larger than {}", ZenGcConfig::GcMaxAttachmentPassCount)); + throw OptionParseException(fmt::format("'--gc-attachment-passes' ('{}') is invalid, maximum is {}.", + ServerOptions.GcConfig.AttachmentPassCount, + ZenGcConfig::GcMaxAttachmentPassCount), + {}); } if (ServerOptions.GcConfig.UseGCV2 == false) { - ZEN_WARN("--gc-v2=false is deprecated, reverting to --gc-v2=true"); + ZEN_WARN("'--gc-v2=false' is deprecated, reverting to '--gc-v2=true'"); ServerOptions.GcConfig.UseGCV2 = true; } } @@ -185,7 +187,7 @@ class CachePolicyOption : public LuaConfig::OptionValue { public: CachePolicyOption(UpstreamCachePolicy& Value) : Value(Value) {} - virtual void Print(std::string_view, zen::StringBuilderBase& StringBuilder) override + virtual void Print(std::string_view, StringBuilderBase& StringBuilder) override { switch (Value) { @@ -232,7 +234,7 @@ class ZenAuthConfigOption : public LuaConfig::OptionValue { public: ZenAuthConfigOption(ZenAuthConfig& Value) : Value(Value) {} - virtual void Print(std::string_view Indent, zen::StringBuilderBase& StringBuilder) override + virtual void Print(std::string_view Indent, StringBuilderBase& StringBuilder) override { if (Value.OpenIdProviders.empty()) { @@ -275,7 +277,7 @@ class ZenObjectStoreConfigOption : public LuaConfig::OptionValue { public: ZenObjectStoreConfigOption(ZenObjectStoreConfig& Value) : Value(Value) {} - virtual void Print(std::string_view Indent, zen::StringBuilderBase& StringBuilder) override + virtual void Print(std::string_view Indent, StringBuilderBase& StringBuilder) override { if (Value.Buckets.empty()) { @@ -318,7 +320,7 @@ class ZenStructuredCacheBucketsConfigOption : public LuaConfig::OptionValue { public: ZenStructuredCacheBucketsConfigOption(std::vector<std::pair<std::string, ZenStructuredCacheBucketConfig>>& Value) : Value(Value) {} - virtual void Print(std::string_view Indent, zen::StringBuilderBase& StringBuilder) override + virtual void Print(std::string_view Indent, StringBuilderBase& StringBuilder) override { if (Value.empty()) { @@ -359,14 +361,15 @@ public: std::string Name = Kv.first.as<std::string>(); if (Name.empty()) { - throw zen::OptionParseException(fmt::format("cache bucket option must have a name.")); + throw OptionParseException("Cache bucket option must have a name.", {}); } const uint64_t MaxBlockSize = Bucket.value().get_or("maxblocksize", BucketConfig.MaxBlockSize); if (MaxBlockSize == 0) { - throw zen::OptionParseException( - fmt::format("maxblocksize option for cache bucket '{}' is invalid. It must be non-zero.", Name)); + throw OptionParseException( + fmt::format("'maxblocksize' option for cache bucket '{}' is invalid. It must be non-zero.", Name), + {}); } BucketConfig.MaxBlockSize = MaxBlockSize; @@ -375,8 +378,9 @@ public: const uint64_t MemCacheSizeThreshold = Bucket.value().get_or("sizethreshold", BucketConfig.MemCacheSizeThreshold); if (MemCacheSizeThreshold == 0) { - throw zen::OptionParseException( - fmt::format("memlayer.sizethreshold option for cache bucket '{}' is invalid. It must be non-zero.", Name)); + throw OptionParseException( + fmt::format("'memlayer.sizethreshold' option for cache bucket '{}' is invalid. It must be non-zero.", Name), + {}); } BucketConfig.MemCacheSizeThreshold = Bucket.value().get_or("sizethreshold", BucketConfig.MemCacheSizeThreshold); } @@ -384,17 +388,20 @@ public: const uint32_t PayloadAlignment = Bucket.value().get_or("payloadalignment", BucketConfig.PayloadAlignment); if (PayloadAlignment == 0 || !IsPow2(PayloadAlignment)) { - throw zen::OptionParseException(fmt::format( - "payloadalignment option for cache bucket '{}' is invalid. It needs to be non-zero and a power of two.", - Name)); + throw OptionParseException( + fmt::format( + "'payloadalignment' option for cache bucket '{}' is invalid. It needs to be non-zero and a power of two.", + Name), + {}); } BucketConfig.PayloadAlignment = PayloadAlignment; const uint64_t LargeObjectThreshold = Bucket.value().get_or("largeobjectthreshold", BucketConfig.LargeObjectThreshold); if (LargeObjectThreshold == 0) { - throw zen::OptionParseException( - fmt::format("largeobjectthreshold option for cache bucket '{}' is invalid. It must be non-zero.", Name)); + throw OptionParseException( + fmt::format("'largeobjectthreshold' option for cache bucket '{}' is invalid. It must be non-zero.", Name), + {}); } BucketConfig.LargeObjectThreshold = LargeObjectThreshold; @@ -409,19 +416,19 @@ public: }; std::shared_ptr<LuaConfig::OptionValue> -MakeOption(zen::UpstreamCachePolicy& Value) +MakeOption(UpstreamCachePolicy& Value) { return std::make_shared<CachePolicyOption>(Value); }; std::shared_ptr<LuaConfig::OptionValue> -MakeOption(zen::ZenAuthConfig& Value) +MakeOption(ZenAuthConfig& Value) { return std::make_shared<ZenAuthConfigOption>(Value); }; std::shared_ptr<LuaConfig::OptionValue> -MakeOption(zen::ZenObjectStoreConfig& Value) +MakeOption(ZenObjectStoreConfig& Value) { return std::make_shared<ZenObjectStoreConfigOption>(Value); }; @@ -660,11 +667,11 @@ ParseConfigFile(const std::filesystem::path& Path, if (!OutputConfigFile.empty()) { - std::filesystem::path WritePath(MakeSafeAbsolutePath(OutputConfigFile)); - zen::ExtendableStringBuilder<512> ConfigStringBuilder; + std::filesystem::path WritePath(MakeSafeAbsolutePath(OutputConfigFile)); + ExtendableStringBuilder<512> ConfigStringBuilder; LuaOptions.Print(ConfigStringBuilder, CmdLineResult); - zen::BasicFile Output; - Output.Open(WritePath, zen::BasicFile::Mode::kTruncate); + BasicFile Output; + Output.Open(WritePath, BasicFile::Mode::kTruncate); Output.Write(ConfigStringBuilder.Data(), ConfigStringBuilder.Size(), 0); } } @@ -737,7 +744,7 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions) const char* DefaultHttp = "asio"; #if ZEN_WITH_HTTPSYS - if (!zen::windows::IsRunningOnWine()) + if (!windows::IsRunningOnWine()) { DefaultHttp = "httpsys"; } @@ -1334,7 +1341,7 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions) } catch (const std::exception& Ex) { - throw zen::OptionParseException(Ex.what()); + throw OptionParseException(Ex.what(), options.help()); } if (Result.count("help")) @@ -1399,17 +1406,17 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions) if (!BaseSnapshotDir.empty()) { if (DataDir.empty()) - throw zen::OptionParseException("You must explicitly specify a data directory when specifying a base snapshot"); + throw OptionParseException("'--snapshot-dir' requires '--data-dir'", options.help()); if (!IsDir(ServerOptions.BaseSnapshotDir)) - throw OptionParseException(fmt::format("Snapshot directory must be a directory: '{}", BaseSnapshotDir)); + throw std::runtime_error(fmt::format("'--snapshot-dir' ('{}') must be a directory", BaseSnapshotDir)); } if (OpenIdProviderUrl.empty() == false) { if (OpenIdClientId.empty()) { - throw zen::OptionParseException("Invalid OpenID client ID"); + throw OptionParseException("'--openid-provider-url' requires '--openid-client-id'", options.help()); } ServerOptions.AuthConfig.OpenIdProviders.push_back( @@ -1436,10 +1443,10 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions) ValidateOptions(ServerOptions); } - catch (const zen::OptionParseException& e) + catch (const OptionParseException& e) { - ZEN_CONSOLE_ERROR("Invalid zenserver arguments: {}\n\n{}", e.what(), options.help()); - + ZEN_CONSOLE("{}\n", options.help()); + ZEN_CONSOLE_ERROR("Invalid zenserver arguments: {}", e.what()); throw; } |