aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2024-09-11 13:53:12 +0200
committerGitHub Enterprise <[email protected]>2024-09-11 13:53:12 +0200
commit5b9d20635600a285cbf35b824e417a0e4c35b735 (patch)
treeeac5484def492d178a6e1558ae2d8200d7b9eeef /src
parentvalidate oplog before opening - if invalid, warn and wipe oplog (#153) (diff)
downloadzen-5b9d20635600a285cbf35b824e417a0e4c35b735.tar.xz
zen-5b9d20635600a285cbf35b824e417a0e4c35b735.zip
oplog cmd improvements (#152)
- Improvement: Removed redundant commands `project-delete` and `oplog-delete`. Use already existing `project-drop` instead. - Improvement: zen oplog commands `project-drop`, `project-info`, `oplog-create`, `oplog-import`, `oplog-mirror` can now help resolve partial project and oplog identifiers - Improvement: zen `oplog-mirror` command now has new filter options to control which files are realized to disk: `--key` for op key, `--file` for file path matching and `--chunk` for chunk id matching - Improvement: `project-drop` command defaults to `--dry-run=true` and will only delete the target if `--dry-run=false` is added to the command line to avoid accidental delete
Diffstat (limited to 'src')
-rw-r--r--src/zen/cmds/projectstore_cmd.cpp509
-rw-r--r--src/zen/cmds/projectstore_cmd.h35
-rw-r--r--src/zen/zen.cpp4
3 files changed, 343 insertions, 205 deletions
diff --git a/src/zen/cmds/projectstore_cmd.cpp b/src/zen/cmds/projectstore_cmd.cpp
index 6591c05cd..55e5299e3 100644
--- a/src/zen/cmds/projectstore_cmd.cpp
+++ b/src/zen/cmds/projectstore_cmd.cpp
@@ -200,6 +200,191 @@ namespace {
}
}
+ std::vector<std::string> GetProjectIds(HttpClient& Http)
+ {
+ std::vector<std::string> AvailableProjects;
+ if (HttpClient::Response Result = Http.Get("/prj"))
+ {
+ CbObject CompactBinary = Result.AsObject();
+ for (CbFieldView Field : CompactBinary.AsFieldView().AsArrayView())
+ {
+ if (auto Id = Field.AsObjectView()["Id"].AsString(); !Id.empty())
+ {
+ AvailableProjects.push_back(std::string(Id));
+ }
+ }
+ }
+ else
+ {
+ Result.ThrowError(fmt::format("failed to fetch available projects from '{}'", Http.GetBaseUri()));
+ }
+ return AvailableProjects;
+ }
+
+ std::vector<std::string> GetOlogIds(HttpClient& Http, std::string_view ProjectId)
+ {
+ std::vector<std::string> AvailableOplogs;
+ if (HttpClient::Response Result = Http.Get(fmt::format("/prj/{}", ProjectId)))
+ {
+ CbObject CompactBinary = Result.AsObject();
+ for (CbFieldView Field : CompactBinary["oplogs"].AsArrayView())
+ {
+ if (auto Id = Field.AsObjectView()["id"].AsString(); !Id.empty())
+ {
+ AvailableOplogs.push_back(std::string(Id));
+ }
+ }
+ }
+ else
+ {
+ Result.ThrowError(fmt::format("failed to fetch available oplogs from '{}' for project '{}'", Http.GetBaseUri(), ProjectId));
+ }
+ return AvailableOplogs;
+ }
+
+ std::vector<std::string> MatchId(const std::vector<std::string>& Ids, std::string_view FindId)
+ {
+ bool FindHasDotDelimiter = FindId.find('.') != std::string_view::npos;
+ std::vector<std::string> PossibleIds;
+ for (const std::string& Id : Ids)
+ {
+ if (Id == FindId)
+ {
+ return std::vector<std::string>{Id};
+ }
+ else if (Id.starts_with(FindId))
+ {
+ if (FindHasDotDelimiter || Id[FindId.length()] == '.')
+ {
+ PossibleIds.push_back(Id);
+ }
+ }
+ }
+ return PossibleIds;
+ }
+
+ std::string FmtArray(const std::vector<std::string>& Values, std::string_view Separator = ", "sv, std::string_view Quotes = "'"sv)
+ {
+ ExtendableStringBuilder<512> SB;
+ for (const std::string& Value : Values)
+ {
+ if (SB.Size() > 0 && !Separator.empty())
+ {
+ SB.Append(Separator);
+ }
+ if (!Quotes.empty())
+ {
+ SB.Append(Quotes);
+ }
+ SB.Append(Value);
+ if (!Quotes.empty())
+ {
+ SB.Append(Quotes);
+ }
+ }
+ return SB.ToString();
+ }
+
+ std::string ResolveProject(HttpClient& Http, std::string_view OptionalProjectName)
+ {
+ std::vector<std::string> AvailableProjects = GetProjectIds(Http);
+ if (AvailableProjects.empty())
+ {
+ if (OptionalProjectName.empty())
+ {
+ ZEN_CONSOLE("No projects found at {}", Http.GetBaseUri());
+ }
+ else
+ {
+ ZEN_CONSOLE("Unable to resolve project name '{}', no projects found at {}", OptionalProjectName, Http.GetBaseUri());
+ }
+ return {};
+ }
+
+ if (OptionalProjectName.empty())
+ {
+ ZEN_CONSOLE("Available projects at {}: {}", Http.GetBaseUri(), FmtArray(AvailableProjects));
+ return {};
+ }
+
+ std::vector<std::string> MatchingProjectIds = MatchId(AvailableProjects, OptionalProjectName);
+ if (MatchingProjectIds.empty())
+ {
+ ZEN_CONSOLE("Unable to match project name '{}' at {}, available projects: {}",
+ OptionalProjectName,
+ Http.GetBaseUri(),
+ FmtArray(AvailableProjects));
+ return {};
+ }
+
+ if (MatchingProjectIds.size() == 1)
+ {
+ return MatchingProjectIds.front();
+ }
+
+ ZEN_CONSOLE("Project name is ambigous '{}' at {}: possible matches: {}",
+ OptionalProjectName,
+ Http.GetBaseUri(),
+ FmtArray(MatchingProjectIds));
+ return {};
+ }
+
+ std::string ResolveOplog(HttpClient& Http, std::string_view ProjectName, std::string_view OptionalOplogName)
+ {
+ std::vector<std::string> AvailableOplogs = GetOlogIds(Http, ProjectName);
+ if (AvailableOplogs.empty())
+ {
+ if (OptionalOplogName.empty())
+ {
+ ZEN_CONSOLE("No oplogs for project '{}' found at {}", ProjectName, Http.GetBaseUri());
+ }
+ else
+ {
+ ZEN_CONSOLE("Unable to resolve oplog name '{}' for project '{}', no oplogs found at {}",
+ OptionalOplogName,
+ ProjectName,
+ Http.GetBaseUri());
+ }
+ return {};
+ }
+
+ if (OptionalOplogName.empty())
+ {
+ if (AvailableOplogs.size() == 1)
+ {
+ return AvailableOplogs.front();
+ }
+ else
+ {
+ ZEN_CONSOLE("Available oplogs for project '{}' at {}: {}", ProjectName, Http.GetBaseUri(), FmtArray(AvailableOplogs));
+ return {};
+ }
+ }
+
+ std::vector<std::string> MatchingOplogIds = MatchId(AvailableOplogs, OptionalOplogName);
+ if (MatchingOplogIds.empty())
+ {
+ ZEN_CONSOLE("Unable to match oplog name '{}' for project '{}', available oplogs at {}: {}",
+ OptionalOplogName,
+ ProjectName,
+ Http.GetBaseUri(),
+ FmtArray(AvailableOplogs));
+ return {};
+ }
+
+ if (MatchingOplogIds.size() == 1)
+ {
+ return MatchingOplogIds.front();
+ }
+
+ ZEN_CONSOLE("Oplog name '{}' for project '{}' at {}, is ambigous, possible matches: {}",
+ OptionalOplogName,
+ ProjectName,
+ Http.GetBaseUri(),
+ FmtArray(MatchingOplogIds));
+ return {};
+ }
+
} // namespace
///////////////////////////////////////
@@ -210,6 +395,7 @@ DropProjectCommand::DropProjectCommand()
m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
m_Options.add_option("", "p", "project", "Project name", cxxopts::value(m_ProjectName), "<projectid>");
m_Options.add_option("", "o", "oplog", "Oplog name", cxxopts::value(m_OplogName), "<oplogid>");
+ m_Options.add_option("", "", "dryrun", "Dry run - resolve arguments but do not drop", cxxopts::value(m_DryRun), "<dryrun>");
m_Options.parse_positional({"project", "oplog"});
m_Options.positional_help("[<projectid> [<oplogid>]]");
}
@@ -235,36 +421,49 @@ DropProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
throw OptionParseException("unable to resolve server specification");
}
+ HttpClient Http(m_HostName);
+
+ m_ProjectName = ResolveProject(Http, m_ProjectName);
if (m_ProjectName.empty())
{
- throw OptionParseException("Drop command requires a project");
+ return 1;
}
- HttpClient Http(m_HostName);
if (m_OplogName.empty())
{
- ZEN_CONSOLE("Dropping project '{}' from '{}'", m_ProjectName, m_HostName);
- if (HttpClient::Response Result = Http.Delete(fmt::format("/prj/{}", m_ProjectName)))
- {
- ZEN_CONSOLE("{}", Result);
- }
- else
+ ZEN_CONSOLE("{} project '{}' from '{}'", m_DryRun ? "Would drop" : "Dropping", m_ProjectName, m_HostName);
+ if (!m_DryRun)
{
- Result.ThrowError("delete project failed"sv);
- return 1;
+ if (HttpClient::Response Result = Http.Delete(fmt::format("/prj/{}", m_ProjectName)))
+ {
+ ZEN_CONSOLE("{}", Result);
+ }
+ else
+ {
+ Result.ThrowError("delete project failed"sv);
+ return 1;
+ }
}
}
else
{
- ZEN_CONSOLE("Dropping oplog '{}/{}' from '{}'", m_ProjectName, m_OplogName, m_HostName);
- if (HttpClient::Response Result = Http.Delete(fmt::format("/prj/{}/oplog/{}", m_ProjectName, m_OplogName)))
+ m_OplogName = ResolveOplog(Http, m_ProjectName, m_OplogName);
+ if (m_OplogName.empty())
{
- ZEN_CONSOLE("{}", Result);
+ return 1;
}
- else
+ ZEN_CONSOLE("{} oplog '{}/{}' from '{}'", m_DryRun ? "Would drop" : "Dropping", m_ProjectName, m_OplogName, m_HostName);
+ if (!m_DryRun)
{
- Result.ThrowError("delete oplog failed"sv);
- return 1;
+ if (HttpClient::Response Result = Http.Delete(fmt::format("/prj/{}/oplog/{}", m_ProjectName, m_OplogName)))
+ {
+ ZEN_CONSOLE("{}", Result);
+ }
+ else
+ {
+ Result.ThrowError("delete oplog failed"sv);
+ return 1;
+ }
}
}
@@ -319,11 +518,29 @@ ProjectInfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
}
else if (m_OplogName.empty())
{
+ m_ProjectName = ResolveProject(Http, m_ProjectName);
+ if (m_ProjectName.empty())
+ {
+ return 1;
+ }
+
Url = fmt::format("/prj/{}", m_ProjectName);
ZEN_CONSOLE("Info on project '{}' from '{}{}'", m_ProjectName, m_HostName, Url);
}
else
{
+ m_ProjectName = ResolveProject(Http, m_ProjectName);
+ if (m_ProjectName.empty())
+ {
+ return 1;
+ }
+
+ m_OplogName = ResolveOplog(Http, m_ProjectName, m_OplogName);
+ if (m_OplogName.empty())
+ {
+ return 1;
+ }
+
Url = fmt::format("/prj/{}/oplog/{}", m_ProjectName, m_OplogName);
ZEN_CONSOLE("Info on oplog '{}/{}' from '{}{}'", m_ProjectName, m_OplogName, m_HostName, Url);
}
@@ -331,13 +548,13 @@ 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;
}
- return 1;
}
///////////////////////////////////////
@@ -417,64 +634,6 @@ CreateProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a
///////////////////////////////////////
-DeleteProjectCommand::DeleteProjectCommand()
-{
- m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
- m_Options.add_option("", "p", "project", "Project name", cxxopts::value(m_ProjectId), "<projectid>");
-}
-
-DeleteProjectCommand::~DeleteProjectCommand() = default;
-
-int
-DeleteProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
-{
- ZEN_UNUSED(GlobalOptions);
-
- using namespace std::literals;
-
- if (!ParseOptions(argc, argv))
- {
- return 0;
- }
-
- m_HostName = ResolveTargetHostSpec(m_HostName);
-
- if (m_HostName.empty())
- {
- throw OptionParseException("unable to resolve server specification");
- }
-
- if (m_ProjectId.empty())
- {
- ZEN_ERROR("Project name must be given");
- return 1;
- }
-
- HttpClient Http(m_HostName);
-
- std::string Url = fmt::format("/prj/{}", m_ProjectId);
-
- if (HttpClient::Response Result = Http.Get(Url, HttpClient::Accept(ZenContentType::kJSON)); !Result)
- {
- Result.ThrowError("failed deleting project"sv);
- return 1;
- }
-
- if (HttpClient::Response Result = Http.Delete(Url, HttpClient::Accept(ZenContentType::kText)))
- {
- ZEN_CONSOLE("{}", Result);
- return 0;
- }
- else
- {
- Result.ThrowError("failed deleting project"sv);
- return 1;
- }
-}
-
-///////////////////////////////////////
-
CreateOplogCommand::CreateOplogCommand()
{
m_Options.add_options()("h,help", "Print help");
@@ -512,13 +671,18 @@ CreateOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
throw OptionParseException("project name must be specified");
}
+ HttpClient Http(m_HostName);
+ m_ProjectId = ResolveProject(Http, m_ProjectId);
+ if (m_ProjectId.empty())
+ {
+ return 1;
+ }
+
if (m_OplogId.empty())
{
throw OptionParseException("oplog name must be specified");
}
- HttpClient Http(m_HostName);
-
std::string Url = fmt::format("/prj/{}/oplog/{}", m_ProjectId, m_OplogId);
if (!m_ForceUpdate)
{
@@ -550,69 +714,6 @@ CreateOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
///////////////////////////////////////
-DeleteOplogCommand::DeleteOplogCommand()
-{
- m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", "Host URL", cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
- m_Options.add_option("", "p", "project", "Project name", cxxopts::value(m_ProjectId), "<projectid>");
- m_Options.add_option("", "o", "oplog", "Oplog name", cxxopts::value(m_OplogId), "<oplogid>");
- m_Options.parse_positional({"project", "oplog", "gcpath"});
-}
-
-DeleteOplogCommand::~DeleteOplogCommand() = default;
-
-int
-DeleteOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
-{
- ZEN_UNUSED(GlobalOptions);
-
- using namespace std::literals;
-
- if (!ParseOptions(argc, argv))
- {
- return 0;
- }
-
- m_HostName = ResolveTargetHostSpec(m_HostName);
-
- if (m_HostName.empty())
- {
- throw OptionParseException("unable to resolve server specification");
- }
-
- if (m_ProjectId.empty())
- {
- throw OptionParseException("project name must be specified");
- }
-
- if (m_OplogId.empty())
- {
- throw OptionParseException("oplog name must be specified");
- }
-
- HttpClient Http(m_HostName);
- std::string Url = fmt::format("/prj/{}/oplog/{}", m_ProjectId, m_OplogId);
-
- if (HttpClient::Response Result = Http.Get(Url, HttpClient::Accept(ZenContentType::kJSON)); !Result)
- {
- Result.ThrowError("failed deleting oplog"sv);
- return 1;
- }
-
- if (HttpClient::Response Result = Http.Delete(Url, HttpClient::Accept(ZenContentType::kText)))
- {
- ZEN_CONSOLE("{}", Result);
- return 0;
- }
- else
- {
- Result.ThrowError("failed deleting oplog"sv);
- return 1;
- }
-}
-
-///////////////////////////////////////
-
ExportOplogCommand::ExportOplogCommand()
{
m_Options.add_options()("h,help", "Print help");
@@ -736,9 +837,17 @@ ExportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
throw OptionParseException("project name must be specified");
}
+ HttpClient Http(m_HostName);
+ m_ProjectName = ResolveProject(Http, m_ProjectName);
+ if (m_ProjectName.empty())
+ {
+ return 1;
+ }
+
+ m_OplogName = ResolveOplog(Http, m_ProjectName, m_OplogName);
if (m_OplogName.empty())
{
- throw OptionParseException("oplog identifier must be specified");
+ return 1;
}
size_t TargetCount = 0;
@@ -794,7 +903,6 @@ ExportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
TargetUrlBase = fmt::format("http://{}", TargetUrlBase);
}
- HttpClient Http(TargetUrlBase);
std::string Url = fmt::format("/prj/{}/oplog/{}", m_ZenProjectName, m_ZenOplogName);
bool CreateOplog = false;
@@ -976,7 +1084,6 @@ ExportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
ZEN_CONSOLE("Saving oplog '{}/{}' from '{}' to {}", m_ProjectName, m_OplogName, m_HostName, TargetDescription);
- HttpClient Http(m_HostName);
if (m_Async)
{
if (HttpClient::Response Result = Http.Post(fmt::format("/prj/{}/oplog/{}/rpc", m_ProjectName, m_OplogName),
@@ -1102,6 +1209,19 @@ ImportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
return 1;
}
+ HttpClient Http(m_HostName);
+ m_ProjectName = ResolveProject(Http, m_ProjectName);
+ if (m_ProjectName.empty())
+ {
+ return 1;
+ }
+
+ m_OplogName = ResolveOplog(Http, m_ProjectName, m_OplogName);
+ if (m_OplogName.empty())
+ {
+ return 1;
+ }
+
size_t TargetCount = 0;
TargetCount += m_CloudUrl.empty() ? 0 : 1;
TargetCount += m_ZenUrl.empty() ? 0 : 1;
@@ -1153,8 +1273,6 @@ ImportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
}
}
- HttpClient Http(m_HostName);
-
std::string Url = fmt::format("/prj/{}/oplog/{}", m_ProjectName, m_OplogName);
bool CreateOplog = false;
@@ -1331,22 +1449,28 @@ SnapshotOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a
throw OptionParseException("unable to resolve server specification");
}
+ HttpClient Http(m_HostName);
+
if (m_ProjectName.empty())
{
ZEN_ERROR("Project name must be given");
return 1;
}
+ m_ProjectName = ResolveProject(Http, m_ProjectName);
+ if (m_ProjectName.empty())
+ {
+ return 1;
+ }
+
+ m_OplogName = ResolveOplog(Http, m_ProjectName, m_OplogName);
if (m_OplogName.empty())
{
- ZEN_ERROR("Oplog name must be given");
return 1;
}
IoBuffer Payload = MakeCbObjectPayload([&](CbObjectWriter& Writer) { Writer.AddString("method"sv, "snapshot"sv); });
- HttpClient Http(m_HostName);
-
ZEN_CONSOLE("Snapshotting oplog '{}/{}' to {}", m_ProjectName, m_OplogName, m_HostName);
if (HttpClient::Response Result =
Http.Post(fmt::format("/prj/{}/oplog/{}/rpc", m_ProjectName, m_OplogName), Payload, HttpClient::Accept(ZenContentType::kJSON)))
@@ -1444,16 +1568,18 @@ ProjectDetailsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char**
throw OptionParseException("unable to resolve server specification");
}
- if (!m_OpId.empty())
+ HttpClient Http(m_HostName);
+
+ if (!m_ProjectName.empty())
{
- if (m_ProjectName.empty() || m_OplogName.empty())
+ m_ProjectName = ResolveProject(Http, m_ProjectName);
+ if (m_ProjectName.empty())
{
- ZEN_ERROR("Provide project and oplog name");
- ZEN_CONSOLE("{}", m_Options.help({""}).c_str());
return 1;
}
}
- else if (!m_OplogName.empty())
+
+ if (!m_OplogName.empty() || !m_OpId.empty())
{
if (m_ProjectName.empty())
{
@@ -1461,9 +1587,19 @@ ProjectDetailsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char**
ZEN_CONSOLE("{}", m_Options.help({""}).c_str());
return 1;
}
+ m_OplogName = ResolveOplog(Http, m_ProjectName, m_OplogName);
+ if (m_OplogName.empty())
+ {
+ return 1;
+ }
}
- HttpClient Http(m_HostName);
+ if (!m_OpId.empty() && m_OplogName.empty())
+ {
+ ZEN_ERROR("Provide project and oplog name");
+ ZEN_CONSOLE("{}", m_Options.help({""}).c_str());
+ return 1;
+ }
ExtendableStringBuilder<128> Url;
Url.Append("/prj/details$");
@@ -1510,6 +1646,24 @@ OplogMirrorCommand::OplogMirrorCommand()
m_Options.add_option("", "p", "project", "Project name to get info from", cxxopts::value(m_ProjectName), "<projectid>");
m_Options.add_option("", "l", "oplog", "Oplog name to get info from", cxxopts::value(m_OplogName), "<oplogid>");
m_Options.add_option("", "t", "target", "Target directory for mirror", cxxopts::value(m_MirrorRootPath), "<path>");
+ m_Options.add_option("",
+ "k",
+ "key",
+ "Oplog key string to limit output (substring match), defaults to no filtering",
+ cxxopts::value(m_KeyFilter),
+ "<key>");
+ m_Options.add_option("",
+ "f",
+ "file",
+ "Oplog file entry path string to limit output (substring match), defaults to no filtering",
+ cxxopts::value(m_FilenameFilter),
+ "<filename>");
+ m_Options.add_option("",
+ "c",
+ "chunk",
+ "Oplog file entry chunk id to limit output, defaults to no filtering",
+ cxxopts::value(m_ChunkIdFilter),
+ "<chunkid>");
m_Options.parse_positional({"project", "oplog", "target"});
m_Options.positional_help("[<projectid> <oplogid> <target>]");
@@ -1536,14 +1690,18 @@ OplogMirrorCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
throw OptionParseException("unable to resolve server specification");
}
+ HttpClient Http(m_HostName);
+
+ m_ProjectName = ResolveProject(Http, m_ProjectName);
if (m_ProjectName.empty())
{
- throw OptionParseException("a project must be specified");
+ return 1;
}
+ m_OplogName = ResolveOplog(Http, m_ProjectName, m_OplogName);
if (m_OplogName.empty())
{
- throw OptionParseException("an oplog must be specified");
+ return 1;
}
if (m_MirrorRootPath.empty())
@@ -1551,21 +1709,17 @@ OplogMirrorCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
throw OptionParseException("a target path must be specified");
}
- ZEN_CONSOLE("Emitting file data from oplog '{}/{}' to '{}'", m_ProjectName, m_OplogName, m_MirrorRootPath);
-
- HttpClient Http(m_HostName);
-
- if (HttpClient::Response Result = Http.Get(fmt::format("/prj/{}/oplog/{}", m_ProjectName, m_OplogName)))
+ Oid ChunkIdFilter = Oid::Zero;
+ if (!m_ChunkIdFilter.empty())
{
- // The info requested is not really used at this moment, we just use the probe to be able to provide
- // better diagnostics up front
+ ChunkIdFilter = Oid::TryFromHexString(m_ChunkIdFilter);
+ if (ChunkIdFilter == Oid::Zero)
+ {
+ throw OptionParseException("chunkid must be an Oid hex string");
+ }
}
- else
- {
- Result.ThrowError("oplog info fetch failed"sv);
- return 1;
- }
+ ZEN_CONSOLE("Emitting file data from oplog '{}/{}' to '{}'", m_ProjectName, m_OplogName, m_MirrorRootPath);
// Emit file data to target directory
@@ -1590,7 +1744,15 @@ OplogMirrorCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
if (CbObjectView Data = DataIter.AsObjectView())
{
std::string FileName = std::string(Data["filename"sv].AsString());
- Oid ChunkId = Data["id"sv].AsObjectId();
+ if (!m_FilenameFilter.empty() && FileName.find(m_FilenameFilter) == std::string::npos)
+ {
+ continue;
+ }
+ Oid ChunkId = Data["id"sv].AsObjectId();
+ if (ChunkIdFilter != Oid::Zero && ChunkIdFilter != ChunkId)
+ {
+ continue;
+ }
if (!FileNames.insert(FileName).second)
{
continue;
@@ -1625,7 +1787,14 @@ OplogMirrorCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
for (auto EntryIter : ResponseObject["entries"sv])
{
CbObjectView Entry = EntryIter.AsObjectView();
-
+ if (!m_KeyFilter.empty())
+ {
+ // ZEN_CONSOLE("{}", Entry["key"].AsString());
+ if (Entry["key"].AsString().find(m_KeyFilter) == std::string_view::npos)
+ {
+ continue;
+ }
+ }
EmitFilesForDataArray(Entry["packagedata"sv].AsArrayView());
EmitFilesForDataArray(Entry["bulkdata"sv].AsArrayView());
diff --git a/src/zen/cmds/projectstore_cmd.h b/src/zen/cmds/projectstore_cmd.h
index 215614b01..8eee570c8 100644
--- a/src/zen/cmds/projectstore_cmd.h
+++ b/src/zen/cmds/projectstore_cmd.h
@@ -25,6 +25,7 @@ private:
std::string m_HostName;
std::string m_ProjectName;
std::string m_OplogName;
+ bool m_DryRun = true;
};
class ProjectInfoCommand : public ProjectStoreCommand
@@ -62,21 +63,6 @@ private:
bool m_ForceUpdate = false;
};
-class DeleteProjectCommand : public ProjectStoreCommand
-{
-public:
- DeleteProjectCommand();
- ~DeleteProjectCommand();
-
- 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-delete", "Delete project and all its oplogs"};
- std::string m_HostName;
- std::string m_ProjectId;
-};
-
class CreateOplogCommand : public ProjectStoreCommand
{
public:
@@ -95,22 +81,6 @@ private:
bool m_ForceUpdate = false;
};
-class DeleteOplogCommand : public ProjectStoreCommand
-{
-public:
- DeleteOplogCommand();
- ~DeleteOplogCommand();
-
- virtual int Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override;
- virtual cxxopts::Options& Options() override { return m_Options; }
-
-private:
- cxxopts::Options m_Options{"oplog-delete", "Delete oplog and all its data"};
- std::string m_HostName;
- std::string m_ProjectId;
- std::string m_OplogId;
-};
-
class ExportOplogCommand : public ProjectStoreCommand
{
public:
@@ -261,6 +231,9 @@ private:
std::string m_ProjectName;
std::string m_OplogName;
std::string m_MirrorRootPath;
+ std::string m_KeyFilter;
+ std::string m_FilenameFilter;
+ std::string m_ChunkIdFilter;
};
} // namespace zen
diff --git a/src/zen/zen.cpp b/src/zen/zen.cpp
index 05af79b31..0f21de130 100644
--- a/src/zen/zen.cpp
+++ b/src/zen/zen.cpp
@@ -295,9 +295,7 @@ main(int argc, char** argv)
CopyCommand CopyCmd;
CopyStateCommand CopyStateCmd;
CreateOplogCommand CreateOplogCmd;
- DeleteOplogCommand DeleteOplogCmd;
CreateProjectCommand CreateProjectCmd;
- DeleteProjectCommand DeleteProjectCmd;
DedupCommand DedupCmd;
DownCommand DownCmd;
DropCommand DropCmd;
@@ -358,7 +356,6 @@ main(int argc, char** argv)
{"jobs", &JobCmd, "Show/cancel zen background jobs"},
{"logs", &LoggingCmd, "Show/control zen logging"},
{"oplog-create", &CreateOplogCmd, "Create a project oplog"},
- {"oplog-delete", &DeleteOplogCmd, "Delete a project oplog"},
{"oplog-export", &ExportOplogCmd, "Export project store oplog"},
{"oplog-import", &ImportOplogCmd, "Import project store oplog"},
{"oplog-mirror", &OplogMirrorCmd, "Mirror project store oplog to file system"},
@@ -366,7 +363,6 @@ main(int argc, char** argv)
{"print", &PrintCmd, "Print compact binary object"},
{"printpackage", &PrintPkgCmd, "Print compact binary package"},
{"project-create", &CreateProjectCmd, "Create a project"},
- {"project-delete", &DeleteProjectCmd, "Delete a project"},
{"project-details", &ProjectDetailsCmd, "Details on project store"},
{"project-drop", &ProjectDropCmd, "Drop project or project oplog"},
{"project-info", &ProjectInfoCmd, "Info on project or project oplog"},