diff options
| author | Dan Engelbrecht <[email protected]> | 2023-01-13 07:08:02 -0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-01-13 07:08:02 -0800 |
| commit | 61f18d2de7f37aa03aa09d55562d05c6da033eb2 (patch) | |
| tree | 217828004ee4417b6f2be80a7bf778b808798e4d | |
| parent | Add info (GET) endpoints for structured cache (#211) (diff) | |
| download | zen-61f18d2de7f37aa03aa09d55562d05c6da033eb2.tar.xz zen-61f18d2de7f37aa03aa09d55562d05c6da033eb2.zip | |
zen command line tool improvements (#212)
- Feature: zen command line tool `cache-info` to show cache, namespace or bucket info
- Feature: zen command line tool `project-info` to show store, project or oplog info
- Feature: zen command line tool `project-drop` to drop project or oplog
- Feature: zen command line tool `gc` to trigger a GC run
- Feature: zen command line tool `gc-info` to check status of GC
- Improvement: zen command line tool now fails on any unrecognized arguments
- Improvement: zen command line tool now displays extra help for all sub-commands
- Improvement: host address can now be configured for zen command line tool `drop` command
changelog
| -rw-r--r-- | CHANGELOG.md | 10 | ||||
| -rw-r--r-- | zen/chunk/chunk.cpp | 5 | ||||
| -rw-r--r-- | zen/chunk/chunk.h | 2 | ||||
| -rw-r--r-- | zen/cmds/cache.cpp | 70 | ||||
| -rw-r--r-- | zen/cmds/cache.h | 21 | ||||
| -rw-r--r-- | zen/cmds/copy.cpp | 9 | ||||
| -rw-r--r-- | zen/cmds/copy.h | 2 | ||||
| -rw-r--r-- | zen/cmds/dedup.cpp | 9 | ||||
| -rw-r--r-- | zen/cmds/dedup.h | 2 | ||||
| -rw-r--r-- | zen/cmds/exportproject.cpp | 7 | ||||
| -rw-r--r-- | zen/cmds/exportproject.h | 2 | ||||
| -rw-r--r-- | zen/cmds/hash.cpp | 5 | ||||
| -rw-r--r-- | zen/cmds/hash.h | 2 | ||||
| -rw-r--r-- | zen/cmds/importproject.cpp | 7 | ||||
| -rw-r--r-- | zen/cmds/importproject.h | 2 | ||||
| -rw-r--r-- | zen/cmds/print.cpp | 22 | ||||
| -rw-r--r-- | zen/cmds/print.h | 4 | ||||
| -rw-r--r-- | zen/cmds/projectstore.cpp | 132 | ||||
| -rw-r--r-- | zen/cmds/projectstore.h | 36 | ||||
| -rw-r--r-- | zen/cmds/run.cpp | 4 | ||||
| -rw-r--r-- | zen/cmds/run.h | 2 | ||||
| -rw-r--r-- | zen/cmds/scrub.cpp | 116 | ||||
| -rw-r--r-- | zen/cmds/scrub.h | 23 | ||||
| -rw-r--r-- | zen/cmds/status.h | 2 | ||||
| -rw-r--r-- | zen/cmds/top.h | 4 | ||||
| -rw-r--r-- | zen/cmds/up.h | 4 | ||||
| -rw-r--r-- | zen/cmds/version.cpp | 7 | ||||
| -rw-r--r-- | zen/cmds/version.h | 2 | ||||
| -rw-r--r-- | zen/zen.cpp | 100 | ||||
| -rw-r--r-- | zen/zen.h | 4 | ||||
| -rw-r--r-- | zenserver/projectstore.cpp | 1 |
31 files changed, 519 insertions, 99 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index d9cf77196..bfce09d7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,15 @@ - `/z$/{namespace}/{bucket}` - get bucket info - Feature: Added project store oplog info: `markerpath`, `totalsize`, `opcount`, `expired` on GET requests for oplog - Feature: Added project store project info: `expired` on GET requests for project -- Feature :Added project store root route `/prj` which is identical to `/prj/list` +- Feature: Added project store root route `/prj` which is identical to `/prj/list` +- Feature: zen command line tool `cache-info` to show cache, namespace or bucket info +- Feature: zen command line tool `project-info` to show store, project or oplog info +- Feature: zen command line tool `project-drop` to drop project or oplog +- Feature: zen command line tool `gc` to trigger a GC run +- Feature: zen command line tool `gc-info` to check status of GC +- Improvement: zen command line tool now fails on any unrecognized arguments +- Improvement: zen command line tool now displays extra help for all sub-commands +- Improvement: host address can now be configured for zen command line tool `drop` command ## 0.2.1 - Feature: Oplog level GC in project store. If gc marker file path is given by UE, oplogs will be GC:d when marker file is deleted (and GC is triggered) diff --git a/zen/chunk/chunk.cpp b/zen/chunk/chunk.cpp index 3fd7c4c1f..d3591f8ca 100644 --- a/zen/chunk/chunk.cpp +++ b/zen/chunk/chunk.cpp @@ -978,7 +978,10 @@ ChunkCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); - auto result = m_Options.parse(argc, argv); + if (!ParseOptions(argc, argv)) + { + return 0; + } bool IsValid = m_ScanDirectory.length(); diff --git a/zen/chunk/chunk.h b/zen/chunk/chunk.h index 32d87b1b7..e796f4147 100644 --- a/zen/chunk/chunk.h +++ b/zen/chunk/chunk.h @@ -12,7 +12,7 @@ public: ~ChunkCommand(); virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } private: cxxopts::Options m_Options{"chunk", "Do a chunking pass"}; diff --git a/zen/cmds/cache.cpp b/zen/cmds/cache.cpp index a4fe4da34..2407f5f55 100644 --- a/zen/cmds/cache.cpp +++ b/zen/cmds/cache.cpp @@ -16,8 +16,10 @@ ZEN_THIRD_PARTY_INCLUDES_END DropCommand::DropCommand() { m_Options.add_options()("h,help", "Print help"); + m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>"); m_Options.add_option("", "n", "namespace", "Namnspace name", cxxopts::value(m_NamespaceName), "<namespacename>"); m_Options.add_option("", "b", "bucket", "Bucket name", cxxopts::value(m_BucketName), "<bucketname>"); + m_Options.parse_positional({"namespace", "bucket"}); } DropCommand::~DropCommand() = default; @@ -25,10 +27,12 @@ DropCommand::~DropCommand() = default; int DropCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { - ZEN_UNUSED(GlobalOptions, argc, argv); + ZEN_UNUSED(GlobalOptions); - m_Options.parse_positional({"namespace", "bucket"}); - m_Options.parse(argc, argv); + if (!ParseOptions(argc, argv)) + { + return 0; + } if (m_NamespaceName.empty()) { @@ -67,3 +71,63 @@ DropCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) return 1; } + +CacheInfoCommand::CacheInfoCommand() +{ + m_Options.add_options()("h,help", "Print help"); + m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>"); + m_Options.add_option("", "n", "namespace", "Namnspace name", cxxopts::value(m_NamespaceName), "<namespacename>"); + m_Options.add_option("", "b", "bucket", "Bucket name", cxxopts::value(m_BucketName), "<bucketname>"); + m_Options.parse_positional({"namespace", "bucket"}); +} + +CacheInfoCommand::~CacheInfoCommand() = default; + +int +CacheInfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) +{ + ZEN_UNUSED(GlobalOptions); + + if (!ParseOptions(argc, argv)) + { + return 0; + } + + cpr::Session Session; + Session.SetHeader(cpr::Header{{"Accept", "application/json"}}); + if (m_HostName.empty()) + { + ZEN_CONSOLE("Info on cache from '{}'", m_HostName); + Session.SetUrl({fmt::format("{}/z$", m_HostName)}); + } + else if (m_BucketName.empty()) + { + ZEN_CONSOLE("Info on cache namespace '{}' from '{}'", m_NamespaceName, m_HostName); + Session.SetUrl({fmt::format("{}/z$/{}", m_HostName, m_NamespaceName)}); + } + else + { + ZEN_CONSOLE("Info on cache bucket '{}/{}' from '{}'", m_NamespaceName, m_BucketName, m_HostName); + Session.SetUrl({fmt::format("{}/z$/{}/{}", m_HostName, m_NamespaceName, m_BucketName)}); + } + + cpr::Response Result = Session.Get(); + + if (zen::IsHttpSuccessCode(Result.status_code)) + { + ZEN_CONSOLE("{}", Result.text); + + return 0; + } + + if (Result.status_code) + { + ZEN_ERROR("Info failed: {}: {} ({})", Result.status_code, Result.reason, Result.text); + } + else + { + ZEN_ERROR("Info failed: {}", Result.error.message); + } + + return 1; +} diff --git a/zen/cmds/cache.h b/zen/cmds/cache.h index 1eb32eb99..915c3d7d3 100644 --- a/zen/cmds/cache.h +++ b/zen/cmds/cache.h @@ -11,11 +11,26 @@ public: ~DropCommand(); virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } private: - cxxopts::Options m_Options{"drop", "Drop one or more cache buckets"}; + cxxopts::Options m_Options{"drop", "Drop cache namespace or bucket"}; + std::string m_HostName; + std::string m_NamespaceName; + std::string m_BucketName; +}; + +class CacheInfoCommand : public ZenCmdBase +{ +public: + CacheInfoCommand(); + ~CacheInfoCommand(); + virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual cxxopts::Options& Options() override { return m_Options; } + +private: + cxxopts::Options m_Options{"cache-info", "Info on cache, namespace or bucket"}; + std::string m_HostName; std::string m_NamespaceName; std::string m_BucketName; - std::string m_HostName{"http://localhost:1337"}; }; diff --git a/zen/cmds/copy.cpp b/zen/cmds/copy.cpp index f3ca242e3..6f6c078d4 100644 --- a/zen/cmds/copy.cpp +++ b/zen/cmds/copy.cpp @@ -16,6 +16,7 @@ CopyCommand::CopyCommand() m_Options.add_option("", "s", "source", "Copy source", cxxopts::value(m_CopySource), "<file/directory>"); m_Options.add_option("", "t", "target", "Copy target", cxxopts::value(m_CopyTarget), "<file/directory>"); m_Options.add_option("", "", "positional", "Positional arguments", cxxopts::value(m_Positional), ""); + m_Options.parse_positional({"source", "target", "positional"}); } CopyCommand::~CopyCommand() = default; @@ -25,14 +26,8 @@ CopyCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); - m_Options.parse_positional({"source", "target", "positional"}); - - auto result = m_Options.parse(argc, argv); - - if (result.count("help")) + if (!ZenCmdBase::ParseOptions(argc, argv)) { - std::cout << m_Options.help({"", "Group"}) << std::endl; - return 0; } diff --git a/zen/cmds/copy.h b/zen/cmds/copy.h index 322cf3f2f..5527ae9b8 100644 --- a/zen/cmds/copy.h +++ b/zen/cmds/copy.h @@ -14,7 +14,7 @@ public: CopyCommand(); ~CopyCommand(); - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; private: diff --git a/zen/cmds/dedup.cpp b/zen/cmds/dedup.cpp index 2b599c307..b48fb8c2d 100644 --- a/zen/cmds/dedup.cpp +++ b/zen/cmds/dedup.cpp @@ -44,6 +44,7 @@ DedupCommand::DedupCommand() m_Options.add_option("", "s", "source", "Copy source", cxxopts::value(m_DedupSource), "<file/directory>"); m_Options.add_option("", "t", "target", "Copy target", cxxopts::value(m_DedupTarget), "<file/directory>"); m_Options.add_option("", "", "positional", "Positional arguments", cxxopts::value(m_Positional), ""); + m_Options.parse_positional({"source", "target", "positional"}); } DedupCommand::~DedupCommand() = default; @@ -53,14 +54,8 @@ DedupCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); - m_Options.parse_positional({"source", "target", "positional"}); - - auto result = m_Options.parse(argc, argv); - - if (result.count("help")) + if (!ParseOptions(argc, argv)) { - std::cout << m_Options.help({"", "Group"}) << std::endl; - return 0; } diff --git a/zen/cmds/dedup.h b/zen/cmds/dedup.h index dbda236f6..6318704f5 100644 --- a/zen/cmds/dedup.h +++ b/zen/cmds/dedup.h @@ -14,7 +14,7 @@ public: DedupCommand(); ~DedupCommand(); - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; private: diff --git a/zen/cmds/exportproject.cpp b/zen/cmds/exportproject.cpp index 8c7c6821b..6925c9f03 100644 --- a/zen/cmds/exportproject.cpp +++ b/zen/cmds/exportproject.cpp @@ -29,6 +29,7 @@ ExportProjectCommand::ExportProjectCommand() m_Options.add_option("", "t", "target", "Target path", cxxopts::value(m_TargetPath), "<targetpath>"); m_Options.add_option("", "p", "project", "Project name", cxxopts::value(m_ProjectName), "<projectname>"); m_Options.add_option("", "o", "oplog", "Oplog name", cxxopts::value(m_OplogNames), "<oplog>"); + m_Options.parse_positional({"target", "project", "oplog"}); } ExportProjectCommand::~ExportProjectCommand() = default; @@ -59,8 +60,10 @@ ExportProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a ZEN_UNUSED(GlobalOptions); - m_Options.parse_positional({"target", "project", "oplog"}); - m_Options.parse(argc, argv); + if (!ParseOptions(argc, argv)) + { + return 0; + } if (m_ProjectName.empty()) { diff --git a/zen/cmds/exportproject.h b/zen/cmds/exportproject.h index bf41e2d7b..3fe7a2263 100644 --- a/zen/cmds/exportproject.h +++ b/zen/cmds/exportproject.h @@ -23,7 +23,7 @@ public: ~ExportProjectCommand(); virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } struct OplogHeader { diff --git a/zen/cmds/hash.cpp b/zen/cmds/hash.cpp index 95f960e29..7987d7738 100644 --- a/zen/cmds/hash.cpp +++ b/zen/cmds/hash.cpp @@ -65,7 +65,10 @@ HashCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); - auto result = m_Options.parse(argc, argv); + if (!ParseOptions(argc, argv)) + { + return 0; + } bool valid = m_ScanDirectory.length(); diff --git a/zen/cmds/hash.h b/zen/cmds/hash.h index 3cc2d1440..e5ee071e9 100644 --- a/zen/cmds/hash.h +++ b/zen/cmds/hash.h @@ -16,7 +16,7 @@ public: ~HashCommand(); virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } private: cxxopts::Options m_Options{"hash", "Hash files"}; diff --git a/zen/cmds/importproject.cpp b/zen/cmds/importproject.cpp index f92e8fcc0..3a2605ebb 100644 --- a/zen/cmds/importproject.cpp +++ b/zen/cmds/importproject.cpp @@ -28,6 +28,7 @@ ImportProjectCommand::ImportProjectCommand() m_Options.add_option("", "s", "source", "Source path", cxxopts::value(m_SourcePath), "<sourcepath>"); m_Options.add_option("", "p", "project", "Project name", cxxopts::value(m_ProjectName), "<projectname>"); m_Options.add_option("", "o", "oplog", "Oplog name", cxxopts::value(m_OplogNames), "<oplog>"); + m_Options.parse_positional({"source", "project", "oplog"}); } ImportProjectCommand::~ImportProjectCommand() = default; @@ -39,8 +40,10 @@ ImportProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a ZEN_UNUSED(GlobalOptions); - m_Options.parse_positional({"source", "project", "oplog"}); - m_Options.parse(argc, argv); + if (!ParseOptions(argc, argv)) + { + return 0; + } if (m_ProjectName.empty()) { diff --git a/zen/cmds/importproject.h b/zen/cmds/importproject.h index 326ccfe92..8d79f06fd 100644 --- a/zen/cmds/importproject.h +++ b/zen/cmds/importproject.h @@ -11,7 +11,7 @@ public: ~ImportProjectCommand(); virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } private: cxxopts::Options m_Options{"import-project", "Import project oplogs from disk"}; diff --git a/zen/cmds/print.cpp b/zen/cmds/print.cpp index 067dc844a..67191605c 100644 --- a/zen/cmds/print.cpp +++ b/zen/cmds/print.cpp @@ -34,6 +34,7 @@ PrintCommand::PrintCommand() { m_Options.add_options()("h,help", "Print help"); m_Options.add_option("", "s", "source", "Object payload file", cxxopts::value(m_Filename), "<file name>"); + m_Options.parse_positional({"source"}); } PrintCommand::~PrintCommand() = default; @@ -41,16 +42,10 @@ PrintCommand::~PrintCommand() = default; int PrintCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { - ZEN_UNUSED(GlobalOptions, argc, argv); - - m_Options.parse_positional({"source"}); + ZEN_UNUSED(GlobalOptions); - auto result = m_Options.parse(argc, argv); - - if (result.count("help")) + if (!ParseOptions(argc, argv)) { - std::cout << m_Options.help({"", "Group"}) << std::endl; - return 0; } @@ -153,6 +148,7 @@ PrintPackageCommand::PrintPackageCommand() { m_Options.add_options()("h,help", "Print help"); m_Options.add_option("", "s", "source", "Package payload file", cxxopts::value(m_Filename), "<file name>"); + m_Options.parse_positional({"source"}); } PrintPackageCommand::~PrintPackageCommand() @@ -162,16 +158,10 @@ PrintPackageCommand::~PrintPackageCommand() int PrintPackageCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { - ZEN_UNUSED(GlobalOptions, argc, argv); - - m_Options.parse_positional({"source"}); + ZEN_UNUSED(GlobalOptions); - auto result = m_Options.parse(argc, argv); - - if (result.count("help")) + if (!ParseOptions(argc, argv)) { - std::cout << m_Options.help({"", "Group"}) << std::endl; - return 0; } diff --git a/zen/cmds/print.h b/zen/cmds/print.h index eed0aa14e..09d91830a 100644 --- a/zen/cmds/print.h +++ b/zen/cmds/print.h @@ -15,7 +15,7 @@ public: ~PrintCommand(); virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } private: cxxopts::Options m_Options{"print", "Print compact binary object"}; @@ -31,7 +31,7 @@ public: ~PrintPackageCommand(); virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } private: cxxopts::Options m_Options{"printpkg", "Print compact binary package"}; diff --git a/zen/cmds/projectstore.cpp b/zen/cmds/projectstore.cpp new file mode 100644 index 000000000..d83bff53c --- /dev/null +++ b/zen/cmds/projectstore.cpp @@ -0,0 +1,132 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "projectstore.h" + +#include <zencore/filesystem.h> +#include <zencore/logging.h> +#include <zenhttp/httpcommon.h> +#include <zenutil/zenserverprocess.h> + +#include <memory> + +ZEN_THIRD_PARTY_INCLUDES_START +#include <cpr/cpr.h> +ZEN_THIRD_PARTY_INCLUDES_END + +DropProjectCommand::DropProjectCommand() +{ + m_Options.add_options()("h,help", "Print help"); + m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>"); + m_Options.add_option("", "p", "project", "Namnspace name", cxxopts::value(m_ProjectName), "<projectname>"); + m_Options.add_option("", "o", "oplog", "Oplog name", cxxopts::value(m_OplogName), "<oplogname>"); + m_Options.parse_positional({"{project}", "{oplog}"}); +} + +DropProjectCommand::~DropProjectCommand() = default; + +int +DropProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) +{ + ZEN_UNUSED(GlobalOptions); + + if (!ParseOptions(argc, argv)) + { + return 0; + } + + if (m_ProjectName.empty()) + { + throw cxxopts::OptionParseException("Drop command requires a project"); + } + + cpr::Session Session; + if (m_OplogName.empty()) + { + ZEN_CONSOLE("Dropping project '{}' from '{}'", m_ProjectName, m_HostName); + Session.SetUrl({fmt::format("{}/prj/{}", m_HostName, m_ProjectName)}); + } + else + { + ZEN_CONSOLE("Dropping oplog '{}/{}' from '{}'", m_ProjectName, m_OplogName, m_HostName); + Session.SetUrl({fmt::format("{}/prj/{}/oplog/{}", m_HostName, m_ProjectName, m_OplogName)}); + } + + cpr::Response Result = Session.Delete(); + + if (zen::IsHttpSuccessCode(Result.status_code)) + { + ZEN_CONSOLE("OK: drop succeeded"); + return 0; + } + + if (Result.status_code) + { + ZEN_ERROR("Drop failed: {}: {} ({})", Result.status_code, Result.reason, Result.text); + } + else + { + ZEN_ERROR("Drop failed: {}", Result.error.message); + } + + return 1; +} + +ProjectInfoCommand::ProjectInfoCommand() +{ + m_Options.add_options()("h,help", "Print help"); + m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>"); + m_Options.add_option("", "p", "project", "Namnspace name", cxxopts::value(m_ProjectName), "<projectname>"); + m_Options.add_option("", "o", "oplog", "Oplog name", cxxopts::value(m_OplogName), "<oplogname>"); + m_Options.parse_positional({"{project}", "{oplog}"}); +} + +ProjectInfoCommand::~ProjectInfoCommand() = default; + +int +ProjectInfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) +{ + ZEN_UNUSED(GlobalOptions); + + if (!ParseOptions(argc, argv)) + { + return 0; + } + + cpr::Session Session; + Session.SetHeader(cpr::Header{{"Accept", "application/json"}}); + if (m_ProjectName.empty()) + { + ZEN_CONSOLE("Info from '{}'", m_HostName); + Session.SetUrl({fmt::format("{}/prj", m_HostName)}); + } + else if (m_OplogName.empty()) + { + ZEN_CONSOLE("Info on project '{}' from '{}'", m_ProjectName, m_HostName); + Session.SetUrl({fmt::format("{}/prj/{}", m_HostName, m_ProjectName)}); + } + else + { + ZEN_CONSOLE("Info on oplog '{}/{}' from '{}'", m_ProjectName, m_OplogName, m_HostName); + Session.SetUrl({fmt::format("{}/prj/{}/oplog/{}", m_HostName, m_ProjectName, m_OplogName)}); + } + + cpr::Response Result = Session.Get(); + + if (zen::IsHttpSuccessCode(Result.status_code)) + { + ZEN_CONSOLE("{}", Result.text); + + return 0; + } + + if (Result.status_code) + { + ZEN_ERROR("Info failed: {}: {} ({})", Result.status_code, Result.reason, Result.text); + } + else + { + ZEN_ERROR("Info failed: {}", Result.error.message); + } + + return 1; +} diff --git a/zen/cmds/projectstore.h b/zen/cmds/projectstore.h new file mode 100644 index 000000000..98e60cb17 --- /dev/null +++ b/zen/cmds/projectstore.h @@ -0,0 +1,36 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "../zen.h" + +class DropProjectCommand : public ZenCmdBase +{ +public: + DropProjectCommand(); + ~DropProjectCommand(); + + virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual cxxopts::Options& Options() override { return m_Options; } + +private: + cxxopts::Options m_Options{"project-drop", "Drop project or project oplog"}; + std::string m_HostName; + std::string m_ProjectName; + std::string m_OplogName; +}; + +class ProjectInfoCommand : public ZenCmdBase +{ +public: + ProjectInfoCommand(); + ~ProjectInfoCommand(); + virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual cxxopts::Options& Options() override { return m_Options; } + +private: + cxxopts::Options m_Options{"project-info", "Info on project or project oplog"}; + std::string m_HostName; + std::string m_ProjectName; + std::string m_OplogName; +}; diff --git a/zen/cmds/run.cpp b/zen/cmds/run.cpp index 6e04d7ebf..6ba74e2fd 100644 --- a/zen/cmds/run.cpp +++ b/zen/cmds/run.cpp @@ -69,6 +69,10 @@ RunCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) Zen1.SetTestDir(TestDir); Zen1.SpawnServer(13337); + if (!ParseOptions(argc, argv)) + { + return 0; + } auto result = m_Options.parse(argc, argv); std::filesystem::path TreePath{m_ExeTree}; diff --git a/zen/cmds/run.h b/zen/cmds/run.h index c5595f235..440ea4ea4 100644 --- a/zen/cmds/run.h +++ b/zen/cmds/run.h @@ -17,7 +17,7 @@ public: ~RunCommand(); virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } private: cxxopts::Options m_Options{"run", "Run command"}; diff --git a/zen/cmds/scrub.cpp b/zen/cmds/scrub.cpp index c0fe8ca61..27ff5e0ac 100644 --- a/zen/cmds/scrub.cpp +++ b/zen/cmds/scrub.cpp @@ -1,7 +1,12 @@ // Copyright Epic Games, Inc. All Rights Reserved. #include "scrub.h" -#include <zenutil/zenserverprocess.h> +#include <zencore/logging.h> +#include <zenhttp/httpcommon.h> + +ZEN_THIRD_PARTY_INCLUDES_START +#include <cpr/cpr.h> +ZEN_THIRD_PARTY_INCLUDES_END using namespace std::literals; @@ -9,6 +14,8 @@ namespace zen { ScrubCommand::ScrubCommand() { + m_Options.add_options()("h,help", "Print help"); + m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>"); } ScrubCommand::~ScrubCommand() = default; @@ -25,6 +32,26 @@ ScrubCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) GcCommand::GcCommand() { + m_Options.add_options()("h,help", "Print help"); + m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>"); + m_Options.add_option("", + "s", + "smallobjects", + "Collect small objects", + cxxopts::value(m_SmallObjects)->default_value("false"), + "<smallobjects>"); + m_Options.add_option("", + "m", + "maxcacheduration", + "Max cache lifetime (in seconds)", + cxxopts::value(m_MaxCacheDuration)->default_value("0"), + "<maxcacheduration>"); + m_Options.add_option("", + "d", + "disksizesoftlimit", + "Max disk usage size (in bytes)", + cxxopts::value(m_DiskSizeSoftLimit)->default_value("0"), + "<disksizesoftlimit>"); } GcCommand::~GcCommand() @@ -36,7 +63,92 @@ GcCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions, argc, argv); - return 0; + if (!ParseOptions(argc, argv)) + { + return 0; + } + + cpr::Parameters Params; + if (m_SmallObjects) + { + Params.Add({"smallobjects", "true"}); + } + if (m_MaxCacheDuration != 0) + { + Params.Add({"maxcacheduration", fmt::format("{}", m_MaxCacheDuration)}); + } + if (m_DiskSizeSoftLimit != 0) + { + Params.Add({"disksizesoftlimit", fmt::format("{}", m_DiskSizeSoftLimit)}); + } + + 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; + } + + if (Result.status_code) + { + ZEN_ERROR("GC start failed: {}: {} ({})", Result.status_code, Result.reason, Result.text); + } + else + { + ZEN_ERROR("GC start failed: {}", Result.error.message); + } + + return 1; +} + +GcStatusCommand::GcStatusCommand() +{ + m_Options.add_options()("h,help", "Print help"); + m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value("http://localhost:1337"), "<hosturl>"); +} + +GcStatusCommand::~GcStatusCommand() +{ +} + +int +GcStatusCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) +{ + ZEN_UNUSED(GlobalOptions, argc, argv); + + if (!ParseOptions(argc, argv)) + { + return 0; + } + + cpr::Session Session; + Session.SetHeader(cpr::Header{{"Accept", "application/json"}}); + Session.SetUrl({fmt::format("{}/admin/gc", m_HostName)}); + + cpr::Response Result = Session.Get(); + + if (zen::IsHttpSuccessCode(Result.status_code)) + { + ZEN_CONSOLE("OK: {}", Result.text); + return 0; + } + + if (Result.status_code) + { + ZEN_ERROR("GC status failed: {}: {} ({})", Result.status_code, Result.reason, Result.text); + } + else + { + ZEN_ERROR("GC status failed: {}", Result.error.message); + } + + return 1; } } // namespace zen diff --git a/zen/cmds/scrub.h b/zen/cmds/scrub.h index 561ae578d..ee8b4fdbb 100644 --- a/zen/cmds/scrub.h +++ b/zen/cmds/scrub.h @@ -15,10 +15,11 @@ public: ~ScrubCommand(); virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } private: cxxopts::Options m_Options{"scrub", "Scrub zen storage"}; + std::string m_HostName; }; /** Garbage collect storage @@ -30,10 +31,28 @@ public: ~GcCommand(); virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } private: cxxopts::Options m_Options{"gc", "Garbage collect zen storage"}; + std::string m_HostName; + bool m_SmallObjects{false}; + uint64_t m_MaxCacheDuration{0}; + uint64_t m_DiskSizeSoftLimit{0}; +}; + +class GcStatusCommand : public ZenCmdBase +{ +public: + GcStatusCommand(); + ~GcStatusCommand(); + + virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; + virtual cxxopts::Options& Options() override { return m_Options; } + +private: + cxxopts::Options m_Options{"gc-status", "Garbage collect zen storage status check"}; + std::string m_HostName; }; } // namespace zen diff --git a/zen/cmds/status.h b/zen/cmds/status.h index acde280c5..98f72e651 100644 --- a/zen/cmds/status.h +++ b/zen/cmds/status.h @@ -13,7 +13,7 @@ public: ~StatusCommand(); virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } private: cxxopts::Options m_Options{"status", "Show zen status"}; diff --git a/zen/cmds/top.h b/zen/cmds/top.h index d8bf91a1c..83410587b 100644 --- a/zen/cmds/top.h +++ b/zen/cmds/top.h @@ -13,7 +13,7 @@ public: ~TopCommand(); virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } private: cxxopts::Options m_Options{"top", "Show dev UI"}; @@ -26,7 +26,7 @@ public: ~PsCommand(); virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } private: cxxopts::Options m_Options{"ps", "Enumerate running Zen server instances"}; diff --git a/zen/cmds/up.h b/zen/cmds/up.h index fe1ed7a0c..c0f1beaba 100644 --- a/zen/cmds/up.h +++ b/zen/cmds/up.h @@ -13,7 +13,7 @@ public: ~UpCommand(); virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } private: cxxopts::Options m_Options{"up", "Bring up zen service"}; @@ -26,7 +26,7 @@ public: ~DownCommand(); virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } private: cxxopts::Options m_Options{"down", "Bring down zen service"}; diff --git a/zen/cmds/version.cpp b/zen/cmds/version.cpp index b92ef670a..ba83b527d 100644 --- a/zen/cmds/version.cpp +++ b/zen/cmds/version.cpp @@ -22,6 +22,7 @@ VersionCommand::VersionCommand() m_Options.add_options()("h,help", "Print help"); m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName), "[hosturl]"); m_Options.add_option("", "d", "detailed", "Detailed Version", cxxopts::value(m_DetailedVersion), "[detailedversion]"); + m_Options.parse_positional({"hosturl"}); } VersionCommand::~VersionCommand() = default; @@ -30,8 +31,10 @@ int VersionCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { ZEN_UNUSED(GlobalOptions); - m_Options.parse_positional({"hosturl"}); - m_Options.parse(argc, argv); + if (!ParseOptions(argc, argv)) + { + return 0; + } std::string Version; diff --git a/zen/cmds/version.h b/zen/cmds/version.h index 17aec73e3..0e37e91a0 100644 --- a/zen/cmds/version.h +++ b/zen/cmds/version.h @@ -13,7 +13,7 @@ public: ~VersionCommand(); virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override; - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } private: cxxopts::Options m_Options{"version", "Get zen service version"}; diff --git a/zen/zen.cpp b/zen/zen.cpp index 447c4f825..b4159df3a 100644 --- a/zen/zen.cpp +++ b/zen/zen.cpp @@ -13,7 +13,9 @@ #include "cmds/hash.h" #include "cmds/importproject.h" #include "cmds/print.h" +#include "cmds/projectstore.h" #include "cmds/run.h" +#include "cmds/scrub.h" #include "cmds/status.h" #include "cmds/top.h" #include "cmds/up.h" @@ -48,7 +50,7 @@ public: return 0; } - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } private: cxxopts::Options m_Options{"template", "EDIT THIS COMMAND DESCRIPTION"}; @@ -57,6 +59,36 @@ private: ////////////////////////////////////////////////////////////////////////// +bool +ZenCmdBase::ParseOptions(int argc, char** argv) +{ + cxxopts::Options& CmdOptions = Options(); + cxxopts::ParseResult Result = CmdOptions.parse(argc, argv); + if (Result.count("help")) + { + printf("%s\n", CmdOptions.help({}).c_str()); + return false; + } + if (!Result.unmatched().empty()) + { + zen::ExtendableStringBuilder<64> StringBuilder; + for (bool First = true; const auto& Param : Result.unmatched()) + { + if (!First) + { + StringBuilder.Append(", "); + } + StringBuilder.Append('"'); + StringBuilder.Append(Param); + StringBuilder.Append('"'); + First = false; + } + throw cxxopts::OptionParseException(fmt::format("Invalid arguments: {}", StringBuilder.ToView())); + } + + return true; +} + #if ZEN_WITH_TESTS class RunTestsCommand : public ZenCmdBase @@ -84,7 +116,7 @@ public: return ZEN_RUN_TESTS(argc, argv); } - virtual cxxopts::Options* Options() override { return &m_Options; } + virtual cxxopts::Options& Options() override { return m_Options; } private: cxxopts::Options m_Options{"runtests", "Run tests"}; @@ -129,6 +161,11 @@ main(int argc, char** argv) ExportProjectCommand ExportProjectCmd; ImportProjectCommand ImportProjectCmd; VersionCommand VersionCmd; + CacheInfoCommand CacheInfoCmd; + DropProjectCommand ProjectDropCmd; + ProjectInfoCommand ProjectInfoCmd; + GcCommand GcCmd; + GcStatusCommand GcStatusCmd; #if ZEN_WITH_TESTS RunTestsCommand RunTestsCmd; @@ -141,28 +178,33 @@ main(int argc, char** argv) const char* CmdSummary; } Commands[] = { // clang-format off -// {"chunk", &ChunkCmd, "Perform chunking"}, - {"copy", &CopyCmd, "Copy file(s)"}, - {"dedup", &DedupCmd, "Dedup files"}, - {"drop", &DropCmd, "Drop cache namespace or bucket"}, - {"export-project", &ExportProjectCmd, "Export project store oplog"}, - {"hash", &HashCmd, "Compute file hashes"}, - {"import-project", &ImportProjectCmd, "Import project store oplog"}, - {"print", &PrintCmd, "Print compact binary object"}, - {"printpackage", &PrintPkgCmd, "Print compact binary package"}, +// {"chunk", &ChunkCmd, "Perform chunking"}, + {"copy", &CopyCmd, "Copy file(s)"}, + {"dedup", &DedupCmd, "Dedup files"}, + {"drop", &DropCmd, "Drop cache namespace or bucket"}, + {"export-project", &ExportProjectCmd, "Export project store oplog"}, + {"hash", &HashCmd, "Compute file hashes"}, + {"import-project", &ImportProjectCmd, "Import project store oplog"}, + {"print", &PrintCmd, "Print compact binary object"}, + {"printpackage", &PrintPkgCmd, "Print compact binary package"}, #if ZEN_WITH_EXEC_SERVICES - {"run", &RunCmd, "Remote execution"}, + {"run", &RunCmd, "Remote execution"}, #endif // ZEN_WITH_EXEC_SERVICES - {"status", &StatusCmd, "Show zen status"}, - {"ps", &PsCmd, "Enumerate running zen server instances"}, - {"top", &TopCmd, "Monitor zen server activity"}, - {"up", &UpCmd, "Bring zen server up"}, - {"down", &DownCmd, "Bring zen server down"}, - {"version", &VersionCmd, "Get zen server version"}, - // clang-format on + {"status", &StatusCmd, "Show zen status"}, + {"ps", &PsCmd, "Enumerate running zen server instances"}, + {"top", &TopCmd, "Monitor zen server activity"}, + {"up", &UpCmd, "Bring zen server up"}, + {"down", &DownCmd, "Bring zen server down"}, + {"version", &VersionCmd, "Get zen server version"}, + {"cache-info", &CacheInfoCmd, "Info on cache, namespace or bucket"}, + {"project-drop", &ProjectDropCmd, "Drop project or project oplog"}, + {"project-info", &ProjectInfoCmd, "Info on project or project oplog"}, + {"gc", &GcCmd, "Garbage collect zen storage"}, + {"gc-status", &GcStatusCmd, "Garbage collect zen storage status check"}, #if ZEN_WITH_TESTS - {"runtests", &RunTestsCmd, "Run zen tests"}, + {"runtests", &RunTestsCmd, "Run zen tests"}, #endif + // clang-format on }; // Build set containing available commands @@ -286,28 +328,18 @@ main(int argc, char** argv) { if (StrCaseCompare(SubCommand.c_str(), CmdInfo.CmdName) == 0) { - cxxopts::Options* VerbOptions = CmdInfo.Cmd->Options(); - + cxxopts::Options& VerbOptions = CmdInfo.Cmd->Options(); try { return CmdInfo.Cmd->Run(GlobalOptions, (int)CommandArgVec.size(), CommandArgVec.data()); } catch (cxxopts::OptionParseException& Ex) { - if (VerbOptions) - { - std::string help = VerbOptions->help(); - - printf("Error parsing arguments for command '%s': %s\n\n%s", SubCommand.c_str(), Ex.what(), help.c_str()); + std::string help = VerbOptions.help(); - exit(11); - } - else - { - printf("Error parsing arguments for command '%s': %s\n\n", SubCommand.c_str(), Ex.what()); + printf("Error parsing arguments for command '%s': %s\n\n%s", SubCommand.c_str(), Ex.what(), help.c_str()); - exit(11); - } + exit(11); } } } @@ -29,5 +29,7 @@ class ZenCmdBase { public: virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) = 0; - virtual cxxopts::Options* Options() = 0; + virtual cxxopts::Options& Options() = 0; + + bool ParseOptions(int argc, char** argv); }; diff --git a/zenserver/projectstore.cpp b/zenserver/projectstore.cpp index d1033dea1..1779bfb3c 100644 --- a/zenserver/projectstore.cpp +++ b/zenserver/projectstore.cpp @@ -2676,6 +2676,7 @@ HttpProjectService::HttpProjectService(CidStore& Store, ProjectStore* Projects) fmt::format("project {} not found", ProjectId)); } + ZEN_INFO("deleting project '{}'", ProjectId); if (!m_ProjectStore->DeleteProject(ProjectId)) { return Req.ServerRequest().WriteResponse(HttpResponseCode::Locked, |