diff options
author | Innokentii Mokin <innokentii@ydb.tech> | 2024-03-13 15:04:44 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-13 15:04:44 +0300 |
commit | d0d041c79c3419ffac59c0e4861a743fd90622ff (patch) | |
tree | 27c0ca317357d9c05d4e423a1cf1aa4187463212 | |
parent | c0a403cc6b0cd7b86a4e0760eaf47af8493e7cfc (diff) | |
download | ydb-d0d041c79c3419ffac59c0e4861a743fd90622ff.tar.gz |
[Config Validation] add config validate command (#2680)
-rw-r--r-- | ydb/core/config/init/init.h | 5 | ||||
-rw-r--r-- | ydb/core/config/init/init_impl.h | 4 | ||||
-rw-r--r-- | ydb/core/config/init/init_noop.cpp | 81 | ||||
-rw-r--r-- | ydb/core/config/init/ya.make | 1 | ||||
-rw-r--r-- | ydb/core/driver_lib/cli_base/cli_cmds_db.cpp | 8 | ||||
-rw-r--r-- | ydb/core/driver_lib/cli_base/cli_command.h | 4 | ||||
-rw-r--r-- | ydb/core/driver_lib/cli_utils/cli_cmds.h | 5 | ||||
-rw-r--r-- | ydb/core/driver_lib/cli_utils/cli_cmds_cms.cpp | 4 | ||||
-rw-r--r-- | ydb/core/driver_lib/cli_utils/cli_cmds_console.cpp | 18 | ||||
-rw-r--r-- | ydb/core/driver_lib/cli_utils/cli_cmds_genconfig.cpp | 4 | ||||
-rw-r--r-- | ydb/core/driver_lib/cli_utils/cli_cmds_root.cpp | 1 | ||||
-rw-r--r-- | ydb/core/driver_lib/cli_utils/cli_cmds_tablet.cpp | 8 | ||||
-rw-r--r-- | ydb/core/driver_lib/cli_utils/cli_cmds_validate_config.cpp | 160 | ||||
-rw-r--r-- | ydb/core/driver_lib/cli_utils/ya.make | 2 | ||||
-rw-r--r-- | ydb/core/driver_lib/run/driver.h | 1 | ||||
-rw-r--r-- | ydb/core/driver_lib/run/main.cpp | 1 | ||||
-rw-r--r-- | ydb/public/lib/ydb_cli/common/command.cpp | 31 | ||||
-rw-r--r-- | ydb/public/lib/ydb_cli/common/command.h | 7 |
18 files changed, 310 insertions, 35 deletions
diff --git a/ydb/core/config/init/init.h b/ydb/core/config/init/init.h index 93ec4136c9..1c19c43026 100644 --- a/ydb/core/config/init/init.h +++ b/ydb/core/config/init/init.h @@ -179,6 +179,11 @@ std::unique_ptr<INodeBrokerClient> MakeDefaultNodeBrokerClient(); std::unique_ptr<IDynConfigClient> MakeDefaultDynConfigClient(); std::unique_ptr<IInitLogger> MakeDefaultInitLogger(); +std::unique_ptr<IMemLogInitializer> MakeNoopMemLogInitializer(); +std::unique_ptr<INodeBrokerClient> MakeNoopNodeBrokerClient(); +std::unique_ptr<IDynConfigClient> MakeNoopDynConfigClient(); +std::unique_ptr<IInitLogger> MakeNoopInitLogger(); + std::unique_ptr<IInitialConfigurator> MakeDefaultInitialConfigurator(TInitialConfiguratorDependencies deps); class TInitialConfigurator { diff --git a/ydb/core/config/init/init_impl.h b/ydb/core/config/init/init_impl.h index 70b042e45c..d92100a0b1 100644 --- a/ydb/core/config/init/init_impl.h +++ b/ydb/core/config/init/init_impl.h @@ -923,7 +923,7 @@ public: Option("naming-file", TCfg::TNameserviceConfigFieldTag{}); CommonAppOptions.NodeId = CommonAppOptions.DeduceNodeId(AppConfig, Env); - Cout << "Determined node ID: " << CommonAppOptions.NodeId << Endl; + Logger.Out() << "Determined node ID: " << CommonAppOptions.NodeId << Endl; CommonAppOptions.ValidateTenant(); @@ -992,7 +992,7 @@ public: TenantName = FillTenantPoolConfig(CommonAppOptions); - Cout << "configured" << Endl; + Logger.Out() << "configured" << Endl; FillData(CommonAppOptions); } diff --git a/ydb/core/config/init/init_noop.cpp b/ydb/core/config/init/init_noop.cpp new file mode 100644 index 0000000000..9d6b653745 --- /dev/null +++ b/ydb/core/config/init/init_noop.cpp @@ -0,0 +1,81 @@ +#include "init.h" + +namespace { + +class TNullStream : public IOutputStream { + void DoWrite(const void*, size_t) override {} +}; + +TNullStream NullStream; + +} // anonymous namespace + +namespace NKikimr::NConfig { + +class TNoopInitLogger + : public IInitLogger +{ +public: + IOutputStream& Out() const noexcept override { + return NullStream; + }; + + IOutputStream& Err() const noexcept override { + return NullStream; + }; +}; + +class TNoopDynConfigClient + : public IDynConfigClient +{ +public: + TMaybe<NKikimr::NClient::TConfigurationResult> GetConfig( + const TGrpcSslSettings&, + const TVector<TString>&, + const TDynConfigSettings&, + const IEnv&, + IInitLogger&) const override + { + return {}; + }; +}; + +class TNoopNodeBrokerClient + : public INodeBrokerClient +{ +public: + std::unique_ptr<INodeRegistrationResult> RegisterDynamicNode( + const TGrpcSslSettings&, + const TVector<TString>&, + const TNodeRegistrationSettings&, + const IEnv&, + IInitLogger&) const override + { + return nullptr; + }; +}; + +class TNoopMemLogInitializer + : public IMemLogInitializer +{ +public: + void Init(const NKikimrConfig::TMemoryLogConfig&) const override {}; +}; + +std::unique_ptr<IMemLogInitializer> MakeNoopMemLogInitializer() { + return std::make_unique<TNoopMemLogInitializer>(); +} + +std::unique_ptr<INodeBrokerClient> MakeNoopNodeBrokerClient() { + return std::make_unique<TNoopNodeBrokerClient>(); +} + +std::unique_ptr<IDynConfigClient> MakeNoopDynConfigClient() { + return std::make_unique<TNoopDynConfigClient>(); +} + +std::unique_ptr<IInitLogger> MakeNoopInitLogger() { + return std::make_unique<TNoopInitLogger>(); +} + +} // namespace NKikimr::NConfig diff --git a/ydb/core/config/init/ya.make b/ydb/core/config/init/ya.make index 839da3a53c..c10c6c3e36 100644 --- a/ydb/core/config/init/ya.make +++ b/ydb/core/config/init/ya.make @@ -3,6 +3,7 @@ LIBRARY() SRCS( init.h init.cpp + init_noop.cpp dummy.h dummy.cpp ) diff --git a/ydb/core/driver_lib/cli_base/cli_cmds_db.cpp b/ydb/core/driver_lib/cli_base/cli_cmds_db.cpp index 5cc0385bbe..e0b2079c70 100644 --- a/ydb/core/driver_lib/cli_base/cli_cmds_db.cpp +++ b/ydb/core/driver_lib/cli_base/cli_cmds_db.cpp @@ -116,10 +116,10 @@ public: } }; -class TClientCommandSchemaExec : public TClientCommandConfig { +class TClientCommandSchemaExec : public TClientCommandBase { public: TClientCommandSchemaExec() - : TClientCommandConfig("execute", { "exec" }, "Execute schema protobuf") + : TClientCommandBase("execute", { "exec" }, "Execute schema protobuf") {} bool ReturnTxId; @@ -1265,10 +1265,10 @@ public: } }; -class TClientCommandDbExec : public TClientCommandConfig { +class TClientCommandDbExec : public TClientCommandBase { public: TClientCommandDbExec() - : TClientCommandConfig("minikql", { "execute", "exec", "mkql" }, "Execute Mini-KQL query") + : TClientCommandBase("minikql", { "execute", "exec", "mkql" }, "Execute Mini-KQL query") {} TString MiniKQL; diff --git a/ydb/core/driver_lib/cli_base/cli_command.h b/ydb/core/driver_lib/cli_base/cli_command.h index dd50de9233..49f026ffc7 100644 --- a/ydb/core/driver_lib/cli_base/cli_command.h +++ b/ydb/core/driver_lib/cli_base/cli_command.h @@ -10,9 +10,9 @@ namespace NDriverClient { using namespace NYdb::NConsoleClient; -class TClientCommandConfig : public TClientCommand { +class TClientCommandBase : public TClientCommand { public: - TClientCommandConfig( + TClientCommandBase( const TString& name, const std::initializer_list<TString>& aliases = std::initializer_list<TString>(), const TString& description = TString() diff --git a/ydb/core/driver_lib/cli_utils/cli_cmds.h b/ydb/core/driver_lib/cli_utils/cli_cmds.h index b25db5a7be..049e592f96 100644 --- a/ydb/core/driver_lib/cli_utils/cli_cmds.h +++ b/ydb/core/driver_lib/cli_utils/cli_cmds.h @@ -46,5 +46,10 @@ public: TClientCommandCms(); }; +class TClientCommandConfig : public TClientCommandTree { +public: + TClientCommandConfig(); +}; + } } diff --git a/ydb/core/driver_lib/cli_utils/cli_cmds_cms.cpp b/ydb/core/driver_lib/cli_utils/cli_cmds_cms.cpp index 3206ff8b21..5f7983f16e 100644 --- a/ydb/core/driver_lib/cli_utils/cli_cmds_cms.cpp +++ b/ydb/core/driver_lib/cli_utils/cli_cmds_cms.cpp @@ -10,7 +10,7 @@ namespace NKikimr { namespace NDriverClient { -class TCmsClientCommand : public TClientCommandConfig { +class TCmsClientCommand : public TClientCommandBase { public: TString Domain; NKikimrClient::TCmsRequest Request; @@ -18,7 +18,7 @@ public: TCmsClientCommand(const TString &name, const std::initializer_list<TString> &aliases, const TString &description) - : TClientCommandConfig(name, aliases, description) + : TClientCommandBase(name, aliases, description) { } diff --git a/ydb/core/driver_lib/cli_utils/cli_cmds_console.cpp b/ydb/core/driver_lib/cli_utils/cli_cmds_console.cpp index 2a810bdd20..8388755abe 100644 --- a/ydb/core/driver_lib/cli_utils/cli_cmds_console.cpp +++ b/ydb/core/driver_lib/cli_utils/cli_cmds_console.cpp @@ -12,7 +12,7 @@ namespace NKikimr { namespace NDriverClient { -class TConsoleClientCommand : public TClientCommandConfig { +class TConsoleClientCommand : public TClientCommandBase { public: TString Domain; ui32 Retries; @@ -21,7 +21,7 @@ public: TConsoleClientCommand(const TString &name, const std::initializer_list<TString> &aliases, const TString &description) - : TClientCommandConfig(name, aliases, description) + : TClientCommandBase(name, aliases, description) , Retries(0) { } @@ -348,11 +348,11 @@ public: } }; -class TClientCommandConvertToYaml: public TClientCommandConfig { +class TClientCommandConvertToYaml: public TClientCommandBase { NKikimrConsole::TConfigureRequest Request; public: TClientCommandConvertToYaml() - : TClientCommandConfig("convert-to-yaml", {}, "Convert config-item to yaml format") + : TClientCommandBase("convert-to-yaml", {}, "Convert config-item to yaml format") { } @@ -418,12 +418,12 @@ public: } }; -class TClientCommandConvertFromYaml: public TClientCommandConfig { +class TClientCommandConvertFromYaml: public TClientCommandBase { TString Request; TString Domain; public: TClientCommandConvertFromYaml() - : TClientCommandConfig("convert-from-yaml", {}, "Convert config-item from yaml format") + : TClientCommandBase("convert-from-yaml", {}, "Convert config-item from yaml format") { } @@ -558,9 +558,9 @@ public: } }; -class TClientCommandConsoleConfig : public TClientCommandTree { +class TClientCommandConsoleConfigTree : public TClientCommandTree { public: - TClientCommandConsoleConfig() + TClientCommandConsoleConfigTree() : TClientCommandTree("config", {}, "") { AddCommand(std::make_unique<TClientCommandConsoleConfigGet>()); @@ -677,7 +677,7 @@ public: TClientCommandConsole::TClientCommandConsole() : TClientCommandTree("console", {}, "Console commands") { - AddCommand(std::make_unique<TClientCommandConsoleConfig>()); + AddCommand(std::make_unique<TClientCommandConsoleConfigTree>()); AddCommand(std::make_unique<TClientCommandConsoleConfigs>()); AddCommand(std::make_unique<TClientCommandConsoleExecute>()); AddCommand(std::make_unique<TClientCommandConsoleValidator>()); diff --git a/ydb/core/driver_lib/cli_utils/cli_cmds_genconfig.cpp b/ydb/core/driver_lib/cli_utils/cli_cmds_genconfig.cpp index 7bab00d10f..0e111f274b 100644 --- a/ydb/core/driver_lib/cli_utils/cli_cmds_genconfig.cpp +++ b/ydb/core/driver_lib/cli_utils/cli_cmds_genconfig.cpp @@ -10,7 +10,7 @@ namespace NKikimr { namespace NDriverClient { -class TClientCommandGenConfigStatic : public TClientCommandConfig { +class TClientCommandGenConfigStatic : public TClientCommandBase { TString ErasureList; TString ErasureStr; @@ -86,7 +86,7 @@ class TClientCommandGenConfigStatic : public TClientCommandConfig { public: TClientCommandGenConfigStatic() - : TClientCommandConfig("static", {}, "Generate configuration for static group") + : TClientCommandBase("static", {}, "Generate configuration for static group") { TStringStream erasureList("{"); for (ui32 species = 0; species < TBlobStorageGroupType::ErasureSpeciesCount; ++species) { diff --git a/ydb/core/driver_lib/cli_utils/cli_cmds_root.cpp b/ydb/core/driver_lib/cli_utils/cli_cmds_root.cpp index eb3777cb6f..b782e638c4 100644 --- a/ydb/core/driver_lib/cli_utils/cli_cmds_root.cpp +++ b/ydb/core/driver_lib/cli_utils/cli_cmds_root.cpp @@ -26,6 +26,7 @@ public: AddCommand(std::make_unique<TClientCommandWhoAmI>()); AddCommand(std::make_unique<TClientCommandDiscovery>()); AddClientCommandServer(*this, std::move(factories)); + AddCommand(std::make_unique<TClientCommandConfig>()); } void Config(TConfig& config) override { diff --git a/ydb/core/driver_lib/cli_utils/cli_cmds_tablet.cpp b/ydb/core/driver_lib/cli_utils/cli_cmds_tablet.cpp index 28b686b61c..252bb38aa8 100644 --- a/ydb/core/driver_lib/cli_utils/cli_cmds_tablet.cpp +++ b/ydb/core/driver_lib/cli_utils/cli_cmds_tablet.cpp @@ -6,10 +6,10 @@ namespace NKikimr { namespace NDriverClient { -class TClientCommandKeyValueRequest : public TClientCommandConfig { +class TClientCommandKeyValueRequest : public TClientCommandBase { public: TClientCommandKeyValueRequest() - : TClientCommandConfig("request", { "req" }, "Request to KV tablet") + : TClientCommandBase("request", { "req" }, "Request to KV tablet") {} TString ProtoBuf; @@ -83,10 +83,10 @@ public: } }; -class TClientCommandTabletExec : public TClientCommandConfig { +class TClientCommandTabletExec : public TClientCommandBase { public: TClientCommandTabletExec() - : TClientCommandConfig("execute", { "exec" }) + : TClientCommandBase("execute", { "exec" }) { } diff --git a/ydb/core/driver_lib/cli_utils/cli_cmds_validate_config.cpp b/ydb/core/driver_lib/cli_utils/cli_cmds_validate_config.cpp new file mode 100644 index 0000000000..f1a1c108db --- /dev/null +++ b/ydb/core/driver_lib/cli_utils/cli_cmds_validate_config.cpp @@ -0,0 +1,160 @@ +#include "cli_cmds.h" + +#include <ydb/core/config/init/init.h> +#include <ydb/core/config/validation/validators.h> + +namespace NKikimr::NDriverClient { + +NKikimrConfig::TAppConfig TransformConfig(const std::vector<TString>& args) { + auto errorCollector = NConfig::MakeDefaultErrorCollector(); + auto protoConfigFileProvider = NConfig::MakeDefaultProtoConfigFileProvider(); + auto configUpdateTracer = NConfig::MakeDefaultConfigUpdateTracer(); + auto memLogInit = NConfig::MakeNoopMemLogInitializer(); + auto nodeBrokerClient = NConfig::MakeNoopNodeBrokerClient(); + auto dynConfigClient = NConfig::MakeNoopDynConfigClient(); + auto env = NConfig::MakeDefaultEnv(); + auto logger = NConfig::MakeNoopInitLogger(); + + NConfig::TInitialConfiguratorDependencies deps{ + *errorCollector, + *protoConfigFileProvider, + *configUpdateTracer, + *memLogInit, + *nodeBrokerClient, + *dynConfigClient, + *env, + *logger, + }; + + auto initCfg = NConfig::MakeDefaultInitialConfigurator(deps); + + std::vector<const char*> argv; + + for (const auto& arg : args) { + argv.push_back(arg.data()); + } + + NLastGetopt::TOpts opts; + initCfg->RegisterCliOptions(opts); + protoConfigFileProvider->RegisterCliOptions(opts); + + NLastGetopt::TOptsParseResult parseResult(&opts, argv.size(), argv.data()); + + initCfg->ValidateOptions(opts, parseResult); + initCfg->Parse(parseResult.GetFreeArgs()); + + NKikimrConfig::TAppConfig appConfig; + ui32 nodeId; + TKikimrScopeId scopeId; + TString tenantName; + TBasicKikimrServicesMask servicesMask; + TMap<TString, TString> labels; + TString clusterName; + NKikimrConfig::TAppConfig initialCmsConfig; + NKikimrConfig::TAppConfig initialCmsYamlConfig; + THashMap<ui32, TConfigItemInfo> configInitInfo; + + initCfg->Apply( + appConfig, + nodeId, + scopeId, + tenantName, + servicesMask, + labels, + clusterName, + initialCmsConfig, + initialCmsYamlConfig, + configInitInfo); + + return appConfig; +} + +void PreFillArgs(std::vector<TString>& args) { + args.push_back("server"); + + args.push_back("--node"); + args.push_back("1"); + + args.push_back("--grpc-port"); + args.push_back("2135"); + + args.push_back("--grpc-port"); + args.push_back("9001"); + + args.push_back("--mon-port"); + args.push_back("8765"); +} + +int ValidateConfigs(const std::vector<TString>& argsCurrent, const std::vector<TString>& argsCandidate) { + auto currentConfig = TransformConfig(argsCurrent); + auto candidateConfig = TransformConfig(argsCandidate); + + std::vector<TString> msgs; + auto res = NConfig::ValidateStaticGroup(currentConfig, candidateConfig, msgs); + + if (res == NConfig::EValidationResult::Warn) { + for (const auto& msg : msgs) { + Cerr << "Warning: " << msg << Endl; + } + + return 2; + } + + if (res == NConfig::EValidationResult::Error) { + for (const auto& msg : msgs) { + Cerr << "Error: " << msg << Endl; + } + + return 1; + } + + Cerr << "OK" << Endl; + + return 0; +} + +class TClientCommandValidateConfig : public TClientCommand { +public: + TClientCommandValidateConfig() + : TClientCommand("validate", {}, "Config validation utils") + {} + + void Config(TConfig& config) override { + TClientCommand::Config(config); + + config.Opts->AddLongOption("current", "Current config") + .Required() + .RequiredArgument("PATH") + .StoreResult(&CurrentConfig); + + config.Opts->AddLongOption("candidate", "Candidate config") + .Required() + .RequiredArgument("PATH") + .StoreResult(&CandidateConfig); + } + + int Run(TConfig& /*config*/) override { + std::vector<TString> argsCurrent; + PreFillArgs(argsCurrent); + argsCurrent.push_back("--yaml-config"); + argsCurrent.push_back(CurrentConfig); + + std::vector<TString> argsCandidate; + PreFillArgs(argsCandidate); + argsCandidate.push_back("--yaml-config"); + argsCandidate.push_back(CandidateConfig); + + return ValidateConfigs(argsCurrent, argsCandidate); + } +private: + TString CurrentConfig; + TString CandidateConfig; +}; + +TClientCommandConfig::TClientCommandConfig() + : TClientCommandTree("config", {}, "config utils") +{ + AddCommand(std::make_unique<TClientCommandValidateConfig>()); +} + +} // NKikimr::NDriverClient diff --git a/ydb/core/driver_lib/cli_utils/ya.make b/ydb/core/driver_lib/cli_utils/ya.make index c75f2845d9..fbcd8f7681 100644 --- a/ydb/core/driver_lib/cli_utils/ya.make +++ b/ydb/core/driver_lib/cli_utils/ya.make @@ -12,6 +12,7 @@ SRCS( cli_cmds_cms.cpp cli_cmds_config.cpp cli_cmds_console.cpp + cli_cmds_validate_config.cpp cli_cmds_debug.cpp cli_cmds_disk.cpp cli_cmds_genconfig.cpp @@ -43,6 +44,7 @@ PEERDIR( ydb/core/client/minikql_compile ydb/core/client/scheme_cache_lib ydb/core/config/init + ydb/core/config/validation ydb/core/driver_lib/cli_base ydb/core/engine ydb/core/erasure diff --git a/ydb/core/driver_lib/run/driver.h b/ydb/core/driver_lib/run/driver.h index 15195f74a6..925215bdd3 100644 --- a/ydb/core/driver_lib/run/driver.h +++ b/ydb/core/driver_lib/run/driver.h @@ -16,6 +16,7 @@ namespace NKikimr { #define MODE_MAP(XX) \ XX(EDM_RUN, "run", "run kikimr node") \ XX(EDM_SERVER, "server", "run kikimr node") \ + XX(EDM_CONFIG, "config", "config utils") \ XX(EDM_ADMIN, "admin", "admin running kikimr") \ XX(EDM_DB, "db", "admin running kikimr") \ XX(EDM_TABLET, "tablet", "admin running kikimr") \ diff --git a/ydb/core/driver_lib/run/main.cpp b/ydb/core/driver_lib/run/main.cpp index df4f8dd8e8..b9fc7b10a4 100644 --- a/ydb/core/driver_lib/run/main.cpp +++ b/ydb/core/driver_lib/run/main.cpp @@ -144,6 +144,7 @@ int MainRun(const TKikimrRunConfig& runConfig, std::shared_ptr<TModuleFactories> case EDM_CMS: case EDM_DISCOVERY: case EDM_WHOAMI: + case EDM_CONFIG: return NDriverClient::NewClient(argc + freeArgsPos, argv - freeArgsPos, factories); case EDM_FORMAT_INFO: return MainFormatInfo(cmdConf, argc, argv); diff --git a/ydb/public/lib/ydb_cli/common/command.cpp b/ydb/public/lib/ydb_cli/common/command.cpp index 8fc45859a9..7a499de82c 100644 --- a/ydb/public/lib/ydb_cli/common/command.cpp +++ b/ydb/public/lib/ydb_cli/common/command.cpp @@ -87,12 +87,17 @@ namespace { } } -TClientCommand::TClientCommand(const TString& name, const std::initializer_list<TString>& aliases, const TString& description) - : Name(name) - , Aliases(aliases) - , Description(description) - , Parent(nullptr) - , Opts(NLastGetopt::TOpts::Default()) +TClientCommand::TClientCommand( + const TString& name, + const std::initializer_list<TString>& aliases, + const TString& description, + bool visible) + : Name(name) + , Aliases(aliases) + , Description(description) + , Visible(visible) + , Parent(nullptr) + , Opts(NLastGetopt::TOpts::Default()) { HideOption("svnrevision"); Opts.AddHelpOption('h'); @@ -398,9 +403,17 @@ void TClientCommandTree::RenderCommandsDescription( const NColorizer::TColors& colors ) { TClientCommand::RenderOneCommandDescription(stream, colors, BEGIN); - for (auto it = SubCommands.begin(); it != SubCommands.end(); ++it) { - bool lastCommand = (std::next(it) == SubCommands.end()); - it->second->RenderOneCommandDescription(stream, colors, lastCommand ? END : MIDDLE); + + TVector<TClientCommand*> VisibleSubCommands; + for (auto& [_, command] : SubCommands) { + if (command->Visible) { + VisibleSubCommands.push_back(command.get()); + } + } + + for (auto it = VisibleSubCommands.begin(); it != VisibleSubCommands.end(); ++it) { + bool lastCommand = (std::next(it) == VisibleSubCommands.end()); + (*it)->RenderOneCommandDescription(stream, colors, lastCommand ? END : MIDDLE); } } diff --git a/ydb/public/lib/ydb_cli/common/command.h b/ydb/public/lib/ydb_cli/common/command.h index ed06e376c5..fb8b4692bb 100644 --- a/ydb/public/lib/ydb_cli/common/command.h +++ b/ydb/public/lib/ydb_cli/common/command.h @@ -23,12 +23,17 @@ public: TString Name; TVector<TString> Aliases; TString Description; + bool Visible = true; const TClientCommand* Parent; NLastGetopt::TOpts Opts; TString Argument; TMap<ui32, TString> Args; - TClientCommand(const TString& name, const std::initializer_list<TString>& aliases = std::initializer_list<TString>(), const TString& description = TString()); + TClientCommand( + const TString& name, + const std::initializer_list<TString>& aliases = std::initializer_list<TString>(), + const TString& description = TString(), + bool visible = true); class TConfig { struct TCommandInfo { |