diff --git a/ydb/core/config/ut/main.cpp b/ydb/core/config/ut/main.cpp new file mode 100644 index 000000000000..9edcba3f7665 --- /dev/null +++ b/ydb/core/config/ut/main.cpp @@ -0,0 +1,338 @@ +#include + +#include +#include +#include + +#include + +#include + +#include + +using namespace NProtoBuf; +using namespace NKikimr::NConfig; + +struct TRequiredFieldInfo { + TString FieldPath; + TVector FieldNumberPath; + + bool operator==(const TRequiredFieldInfo& other) const { + return FieldPath == other.FieldPath && FieldNumberPath == other.FieldNumberPath; + } +}; + +template<> +struct std::hash +{ + std::size_t operator()(const TRequiredFieldInfo& s) const noexcept + { + size_t hash = THash()(s.FieldPath); + + for (auto number : s.FieldNumberPath) { + hash = CombineHashes(hash, THash()(number)); + } + + return hash; + } +}; + +class TConfigDumper { +private: + TString PrintLabel(const FieldDescriptor& field) const { + NColorizer::TColors colors = NColorizer::AutoColors(Cout); + switch (field.label()) { + case FieldDescriptor::LABEL_REQUIRED: + return UseColors ? (TString(colors.Red()) + "required" + colors.Reset()) : TString("required"); + case FieldDescriptor::LABEL_OPTIONAL: + return "optional"; + case FieldDescriptor::LABEL_REPEATED: + return "repeated"; + default: + Y_ABORT("unexpected"); + } + } + + TString Loop() const { + NColorizer::TColors colors = NColorizer::AutoColors(Cout); + return UseColors ? (TString(colors.Red()) + " <- loop" + colors.Reset()) : TString(" <- loop"); + } + + TString FieldName(const FieldDescriptor* field) const { + TString name = field->name(); + if (UseYamlNames) { + NProtobufJson::ToSnakeCaseDense(&name); + } + return name; + } + + TString RootName() const { + return UseYamlNames ? "app_config" : "AppConfig"; + } + + TString DescriptorName(const Descriptor* d) const { + return UseFullyQualifiedTypes ? d->full_name() : d->name(); + } + + TString ConstructFieldPath(const TDeque& fieldPath, const FieldDescriptor* field, ssize_t from = 1, ssize_t to = -1) const { + to = to == -1 ? fieldPath.size() : to; + TVector path; + path.push_back(TString("/") + RootName()); + for (ssize_t i = from; i < to; ++i) { + TString fieldName = FieldName(fieldPath[i]) + (UsePrintArrays && fieldPath[i]->is_repeated() ? "[]" : ""); + path.push_back(fieldName); + } + path.push_back(FieldName(field)); + + return JoinSeq("/", path); + } + + TRequiredFieldInfo ConstructFullFieldPath(const TDeque& fieldPath, const FieldDescriptor* field) const { + TRequiredFieldInfo info; + + TVector path; + path.push_back(TString("/") + RootName()); + for (size_t i = 1; i < fieldPath.size(); ++i) { + TString fieldName = FieldName(fieldPath[i]) + (UsePrintArrays && fieldPath[i]->is_repeated() ? "[]" : ""); + path.push_back(fieldName); + info.FieldNumberPath.push_back(fieldPath[i]->number()); + } + path.push_back(FieldName(field)); + info.FieldNumberPath.push_back(field->number()); + + info.FieldPath = JoinSeq("/", path); + + return info; + } + +public: + void PrintFullTree() const { + Traverse([this](const Descriptor* d, const TDeque& typePath, const TDeque& fieldPath, const FieldDescriptor* field, ssize_t loop) { + Y_UNUSED(fieldPath); + std::cout << TString(typePath.size() * 4, ' ') << + (field ? PrintLabel(*field) : "") + << " " << (d ? DescriptorName(d) : field->cpp_type_name()) + << " " << (field ? FieldName(field) : RootName()) + << " = " << (field ? field->number() : 0) << ";" + << (loop != -1 ? Loop() : "") << std::endl; + }); + } + + void PrintLoops() const { + Traverse([this](const Descriptor* d, const TDeque& typePath, const TDeque& fieldPath, const FieldDescriptor* field, ssize_t loop) { + if (loop != -1) { + std::cout << "Loop at \"" << ConstructFieldPath(fieldPath, field, 1, loop + 1) << "\", types chain: "; + + TVector path; + for (size_t i = loop; i < typePath.size(); ++i) { + path.push_back(DescriptorName(typePath[i])); + } + path.push_back(DescriptorName(d)); + std::cout << JoinSeq(" -> ", path) << std::endl; + } + }); + + } + + TMap>>> DumpRequired() const { + TMap>>> fields; + Traverse([&fields, this](const Descriptor* d, const TDeque& typePath, const TDeque& fieldPath, const FieldDescriptor* field, ssize_t loop) { + Y_UNUSED(d, loop); + if (field && field->is_required()) { + fields[typePath.back()->file()][typePath.back()][field].insert(ConstructFullFieldPath(fieldPath, field)); + } + }); + return fields; + } + + void PrintRequired(const TMap>>>& fields) const { + for (const auto& [file, typeToField] : fields) { + for (const auto& [type, fieldToPaths] : typeToField) { + for (const auto& [field, paths] : fieldToPaths) { + SourceLocation sl; + bool hasSourceInfo = field->GetSourceLocation(&sl); + std::cout << "Required field \"" << FieldName(field) << "\" in " + << DescriptorName(type) << " at $/" << file->name() + << ":" << (hasSourceInfo ? sl.start_line + 1 : 0) + << "\n it can be accessed in config by following paths:" << std::endl; + for (const auto& path : paths) { + std::cout << TString(8, ' ') << path.FieldPath << std::endl; + } + } + } + } + } + + bool UseYamlNames = false; + bool UsePrintArrays = false; + bool UseColors = true; + bool UseFullyQualifiedTypes = true; +}; + +Y_UNIT_TEST_SUITE(ConfigProto) { + Y_UNIT_TEST(ForbidNewRequired) { + TConfigDumper dumper; + auto fields = dumper.DumpRequired(); + + TSet allowedPaths = { + "/AppConfig/AuthConfig/LdapAuthentication/BaseDn/BaseDn", + "/AppConfig/AuthConfig/LdapAuthentication/BindDn/BindDn", + "/AppConfig/AuthConfig/LdapAuthentication/BindPassword/BindPassword", + "/AppConfig/QueryServiceConfig/Generic/DefaultSettings/Activation/ByHour/Hour/Hour", + "/AppConfig/QueryServiceConfig/S3/ClusterMapping/Settings/Activation/ByHour/Hour/Hour", + "/AppConfig/FederatedQueryConfig/Gateways/Generic/DefaultSettings/Activation/ByHour/Hour/Hour", + "/AppConfig/FederatedQueryConfig/Gateways/YqlCore/Flags/Activation/ByHour/Hour/Hour", + "/AppConfig/FederatedQueryConfig/Gateways/Solomon/DefaultSettings/Activation/ByHour/Hour/Hour", + "/AppConfig/FederatedQueryConfig/Gateways/Dq/DefaultSettings/Activation/ByHour/Hour/Hour", + "/AppConfig/FederatedQueryConfig/Gateways/Solomon/ClusterMapping/Settings/Activation/ByHour/Hour/Hour", + "/AppConfig/FederatedQueryConfig/Gateways/S3/DefaultSettings/Activation/ByHour/Hour/Hour", + "/AppConfig/FederatedQueryConfig/Gateways/Ydb/ClusterMapping/Settings/Activation/ByHour/Hour/Hour", + "/AppConfig/FederatedQueryConfig/Gateways/Pq/DefaultSettings/Activation/ByHour/Hour/Hour", + "/AppConfig/FederatedQueryConfig/Gateways/Pq/ClusterMapping/Settings/Activation/ByHour/Hour/Hour", + "/AppConfig/FederatedQueryConfig/Gateways/S3/ClusterMapping/Settings/Activation/ByHour/Hour/Hour", + "/AppConfig/QueryServiceConfig/S3/DefaultSettings/Activation/ByHour/Hour/Hour", + "/AppConfig/FederatedQueryConfig/Gateways/Dq/HiddenActivation/ByHour/Hour/Hour", + "/AppConfig/FederatedQueryConfig/Gateways/Ydb/DefaultSettings/Activation/ByHour/Hour/Hour", + "/AppConfig/QueryServiceConfig/Generic/DefaultSettings/Activation/ByHour/Percentage/Percentage", + "/AppConfig/QueryServiceConfig/S3/DefaultSettings/Activation/ByHour/Percentage/Percentage", + "/AppConfig/QueryServiceConfig/S3/ClusterMapping/Settings/Activation/ByHour/Percentage/Percentage", + "/AppConfig/FederatedQueryConfig/Gateways/Solomon/ClusterMapping/Settings/Activation/ByHour/Percentage/Percentage", + "/AppConfig/FederatedQueryConfig/Gateways/YqlCore/Flags/Activation/ByHour/Percentage/Percentage", + "/AppConfig/FederatedQueryConfig/Gateways/S3/ClusterMapping/Settings/Activation/ByHour/Percentage/Percentage", + "/AppConfig/FederatedQueryConfig/Gateways/Pq/ClusterMapping/Settings/Activation/ByHour/Percentage/Percentage", + "/AppConfig/FederatedQueryConfig/Gateways/Ydb/DefaultSettings/Activation/ByHour/Percentage/Percentage", + "/AppConfig/FederatedQueryConfig/Gateways/Generic/DefaultSettings/Activation/ByHour/Percentage/Percentage", + "/AppConfig/FederatedQueryConfig/Gateways/Pq/DefaultSettings/Activation/ByHour/Percentage/Percentage", + "/AppConfig/FederatedQueryConfig/Gateways/S3/DefaultSettings/Activation/ByHour/Percentage/Percentage", + "/AppConfig/FederatedQueryConfig/Gateways/Ydb/ClusterMapping/Settings/Activation/ByHour/Percentage/Percentage", + "/AppConfig/FederatedQueryConfig/Gateways/Dq/HiddenActivation/ByHour/Percentage/Percentage", + "/AppConfig/FederatedQueryConfig/Gateways/Solomon/DefaultSettings/Activation/ByHour/Percentage/Percentage", + "/AppConfig/FederatedQueryConfig/Gateways/Dq/DefaultSettings/Activation/ByHour/Percentage/Percentage", + "/AppConfig/QueryServiceConfig/S3/DefaultSettings/Name/Name", + "/AppConfig/FederatedQueryConfig/Gateways/Dq/DefaultSettings/Name/Name", + "/AppConfig/QueryServiceConfig/S3/ClusterMapping/Settings/Name/Name", + "/AppConfig/FederatedQueryConfig/Gateways/Solomon/ClusterMapping/Settings/Name/Name", + "/AppConfig/QueryServiceConfig/Generic/DefaultSettings/Name/Name", + "/AppConfig/FederatedQueryConfig/Gateways/S3/DefaultSettings/Name/Name", + "/AppConfig/FederatedQueryConfig/Gateways/Generic/DefaultSettings/Name/Name", + "/AppConfig/FederatedQueryConfig/Gateways/Solomon/DefaultSettings/Name/Name", + "/AppConfig/FederatedQueryConfig/Gateways/S3/ClusterMapping/Settings/Name/Name", + "/AppConfig/FederatedQueryConfig/Gateways/Ydb/ClusterMapping/Settings/Name/Name", + "/AppConfig/FederatedQueryConfig/Gateways/Ydb/DefaultSettings/Name/Name", + "/AppConfig/FederatedQueryConfig/Gateways/Pq/ClusterMapping/Settings/Name/Name", + "/AppConfig/FederatedQueryConfig/Gateways/Pq/DefaultSettings/Name/Name", + "/AppConfig/QueryServiceConfig/Generic/DefaultSettings/Value/Value", + "/AppConfig/FederatedQueryConfig/Gateways/S3/ClusterMapping/Settings/Value/Value", + "/AppConfig/QueryServiceConfig/S3/ClusterMapping/Settings/Value/Value", + "/AppConfig/FederatedQueryConfig/Gateways/Generic/DefaultSettings/Value/Value", + "/AppConfig/FederatedQueryConfig/Gateways/Solomon/DefaultSettings/Value/Value", + "/AppConfig/FederatedQueryConfig/Gateways/S3/DefaultSettings/Value/Value", + "/AppConfig/FederatedQueryConfig/Gateways/Ydb/DefaultSettings/Value/Value", + "/AppConfig/FederatedQueryConfig/Gateways/Solomon/ClusterMapping/Settings/Value/Value", + "/AppConfig/QueryServiceConfig/S3/DefaultSettings/Value/Value", + "/AppConfig/FederatedQueryConfig/Gateways/Dq/DefaultSettings/Value/Value", + "/AppConfig/FederatedQueryConfig/Gateways/Ydb/ClusterMapping/Settings/Value/Value", + "/AppConfig/FederatedQueryConfig/Gateways/Pq/DefaultSettings/Value/Value", + "/AppConfig/FederatedQueryConfig/Gateways/Pq/ClusterMapping/Settings/Value/Value", + "/AppConfig/FederatedQueryConfig/Gateways/YqlCore/Flags/Name/Name", + "/AppConfig/FederatedQueryConfig/Gateways/Solomon/ClusterMapping/Path/Project/Project", + "/AppConfig/FederatedQueryConfig/Gateways/Solomon/ClusterMapping/Path/Cluster/Cluster", + "/AppConfig/FederatedQueryConfig/Gateways/Dq/WithHiddenByHour/Hour/Hour", + "/AppConfig/FederatedQueryConfig/Gateways/Dq/DefaultAutoByHour/Hour/Hour", + "/AppConfig/FederatedQueryConfig/Gateways/Dq/WithHiddenByHour/Percentage/Percentage", + "/AppConfig/FederatedQueryConfig/Gateways/Dq/DefaultAutoByHour/Percentage/Percentage", + "/AppConfig/KQPConfig/Settings/Name/Name", + "/AppConfig/KQPConfig/Settings/Value/Value", + "/AppConfig/ActorSystemConfig/ServiceExecutor/ServiceName/ServiceName", + "/AppConfig/ActorSystemConfig/ServiceExecutor/ExecutorId/ExecutorId", + "/AppConfig/SqsConfig/AuthConfig/OauthToken/TokenFile/TokenFile", + "/AppConfig/SqsConfig/AuthConfig/Jwt/JwtFile/JwtFile", + }; + + TSet> allowedNumberPaths = { + {30, 74, 3, 3}, // /AppConfig/AuthConfig/LdapAuthentication/BaseDn/BaseDn + {30, 74, 4, 4}, // /AppConfig/AuthConfig/LdapAuthentication/BindDn/BindDn + {30, 74, 5, 5}, // /AppConfig/AuthConfig/LdapAuthentication/BindPassword/BindPassword + {73, 6, 100, 3, 2, 1, 1}, // /AppConfig/QueryServiceConfig/S3/DefaultSettings/Activation/ByHour/Hour/Hour + {73, 6, 1, 100, 3, 2, 1, 1}, // /AppConfig/QueryServiceConfig/S3/ClusterMapping/Settings/Activation/ByHour/Hour/Hour + {58, 9, 10, 6, 3, 2, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Generic/DefaultSettings/Activation/ByHour/Hour/Hour + {58, 9, 6, 2, 3, 2, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Solomon/DefaultSettings/Activation/ByHour/Hour/Hour + {58, 9, 9, 1, 3, 2, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/YqlCore/Flags/Activation/ByHour/Hour/Hour + {58, 9, 5, 100, 3, 2, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/S3/DefaultSettings/Activation/ByHour/Hour/Hour + {58, 9, 5, 1, 100, 3, 2, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/S3/ClusterMapping/Settings/Activation/ByHour/Hour/Hour + {73, 11, 6, 3, 2, 1, 1}, // /AppConfig/QueryServiceConfig/Generic/DefaultSettings/Activation/ByHour/Hour/Hour + {58, 9, 4, 4, 3, 2, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Ydb/DefaultSettings/Activation/ByHour/Hour/Hour + {58, 9, 4, 1, 100, 3, 2, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Ydb/ClusterMapping/Settings/Activation/ByHour/Hour/Hour + {58, 9, 6, 1, 100, 3, 2, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Solomon/ClusterMapping/Settings/Activation/ByHour/Hour/Hour + {58, 9, 2, 9, 2, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Dq/HiddenActivation/ByHour/Hour/Hour + {58, 9, 3, 100, 3, 2, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Pq/DefaultSettings/Activation/ByHour/Hour/Hour + {58, 9, 3, 1, 100, 3, 2, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Pq/ClusterMapping/Settings/Activation/ByHour/Hour/Hour + {58, 9, 2, 102, 3, 2, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Dq/DefaultSettings/Activation/ByHour/Hour/Hour + {73, 11, 6, 3, 2, 2, 2}, // /AppConfig/QueryServiceConfig/Generic/DefaultSettings/Activation/ByHour/Percentage/Percentage + {73, 6, 100, 3, 2, 2, 2}, // /AppConfig/QueryServiceConfig/S3/DefaultSettings/Activation/ByHour/Percentage/Percentage + {73, 6, 1, 100, 3, 2, 2, 2}, // /AppConfig/QueryServiceConfig/S3/ClusterMapping/Settings/Activation/ByHour/Percentage/Percentage + {58, 9, 10, 6, 3, 2, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Generic/DefaultSettings/Activation/ByHour/Percentage/Percentage + {58, 9, 5, 100, 3, 2, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/S3/DefaultSettings/Activation/ByHour/Percentage/Percentage + {58, 9, 9, 1, 3, 2, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/YqlCore/Flags/Activation/ByHour/Percentage/Percentage + {58, 9, 5, 1, 100, 3, 2, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/S3/ClusterMapping/Settings/Activation/ByHour/Percentage/Percentage + {58, 9, 4, 4, 3, 2, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Ydb/DefaultSettings/Activation/ByHour/Percentage/Percentage + {58, 9, 2, 102, 3, 2, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Dq/DefaultSettings/Activation/ByHour/Percentage/Percentage + {58, 9, 4, 1, 100, 3, 2, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Ydb/ClusterMapping/Settings/Activation/ByHour/Percentage/Percentage + {58, 9, 6, 2, 3, 2, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Solomon/DefaultSettings/Activation/ByHour/Percentage/Percentage + {58, 9, 6, 1, 100, 3, 2, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Solomon/ClusterMapping/Settings/Activation/ByHour/Percentage/Percentage + {58, 9, 3, 100, 3, 2, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Pq/DefaultSettings/Activation/ByHour/Percentage/Percentage + {58, 9, 3, 1, 100, 3, 2, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Pq/ClusterMapping/Settings/Activation/ByHour/Percentage/Percentage + {58, 9, 2, 9, 2, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Dq/HiddenActivation/ByHour/Percentage/Percentage + {73, 11, 6, 1, 1}, // /AppConfig/QueryServiceConfig/Generic/DefaultSettings/Name/Name + {73, 6, 100, 1, 1}, // /AppConfig/QueryServiceConfig/S3/DefaultSettings/Name/Name + {58, 9, 10, 6, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Generic/DefaultSettings/Name/Name + {58, 9, 4, 1, 100, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Ydb/ClusterMapping/Settings/Name/Name + {73, 6, 1, 100, 1, 1}, // /AppConfig/QueryServiceConfig/S3/ClusterMapping/Settings/Name/Name + {58, 9, 6, 1, 100, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Solomon/ClusterMapping/Settings/Name/Name + {58, 9, 5, 100, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/S3/DefaultSettings/Name/Name + {58, 9, 6, 2, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Solomon/DefaultSettings/Name/Name + {58, 9, 5, 1, 100, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/S3/ClusterMapping/Settings/Name/Name + {58, 9, 3, 100, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Pq/DefaultSettings/Name/Name + {58, 9, 4, 4, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Ydb/DefaultSettings/Name/Name + {58, 9, 3, 1, 100, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Pq/ClusterMapping/Settings/Name/Name + {58, 9, 2, 102, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Dq/DefaultSettings/Name/Name + {73, 6, 100, 2, 2}, // /AppConfig/QueryServiceConfig/S3/DefaultSettings/Value/Value + {73, 11, 6, 2, 2}, // /AppConfig/QueryServiceConfig/Generic/DefaultSettings/Value/Value + {58, 9, 6, 1, 100, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Solomon/ClusterMapping/Settings/Value/Value + {58, 9, 5, 1, 100, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/S3/ClusterMapping/Settings/Value/Value + {58, 9, 6, 2, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Solomon/DefaultSettings/Value/Value + {58, 9, 4, 4, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Ydb/DefaultSettings/Value/Value + {58, 9, 4, 1, 100, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Ydb/ClusterMapping/Settings/Value/Value + {73, 6, 1, 100, 2, 2}, // /AppConfig/QueryServiceConfig/S3/ClusterMapping/Settings/Value/Value + {58, 9, 5, 100, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/S3/DefaultSettings/Value/Value + {58, 9, 2, 102, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Dq/DefaultSettings/Value/Value + {58, 9, 3, 100, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Pq/DefaultSettings/Value/Value + {58, 9, 3, 1, 100, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Pq/ClusterMapping/Settings/Value/Value + {58, 9, 10, 6, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Generic/DefaultSettings/Value/Value + {58, 9, 9, 1, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/YqlCore/Flags/Name/Name + {58, 9, 6, 1, 8, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Solomon/ClusterMapping/Path/Project/Project + {58, 9, 6, 1, 8, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Solomon/ClusterMapping/Path/Cluster/Cluster + {58, 9, 2, 6, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Dq/WithHiddenByHour/Hour/Hour + {58, 9, 2, 2, 1, 1}, // /AppConfig/FederatedQueryConfig/Gateways/Dq/DefaultAutoByHour/Hour/Hour + {58, 9, 2, 6, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Dq/WithHiddenByHour/Percentage/Percentage + {58, 9, 2, 2, 2, 2}, // /AppConfig/FederatedQueryConfig/Gateways/Dq/DefaultAutoByHour/Percentage/Percentage + {17, 10, 1, 1}, // /AppConfig/KQPConfig/Settings/Name/Name + {17, 10, 2, 2}, // /AppConfig/KQPConfig/Settings/Value/Value + {1, 7, 1, 1}, // /AppConfig/ActorSystemConfig/ServiceExecutor/ServiceName/ServiceName + {1, 7, 2, 2}, // /AppConfig/ActorSystemConfig/ServiceExecutor/ExecutorId/ExecutorId + {27, 67, 1, 1, 1}, // /AppConfig/SqsConfig/AuthConfig/OauthToken/TokenFile/TokenFile + {27, 67, 2, 1, 1}, // /AppConfig/SqsConfig/AuthConfig/Jwt/JwtFile/JwtFile + }; + + for (const auto& [file, typeToField] : fields) { + for (const auto& [type, fieldToPaths] : typeToField) { + for (const auto& [field, paths] : fieldToPaths) { + for (const auto& path : paths) { + UNIT_ASSERT_C(allowedPaths.contains(path.FieldPath), Sprintf("Adding new required fields in config is not allowed: %s", path.FieldPath.c_str())); + UNIT_ASSERT_C(allowedNumberPaths.contains(path.FieldNumberPath), Sprintf("Adding new required fields in config is not allowed: %s", path.FieldPath.c_str())); + } + } + } + } + } +} diff --git a/ydb/core/config/ut/ya.make b/ydb/core/config/ut/ya.make new file mode 100644 index 000000000000..983daef7f5fa --- /dev/null +++ b/ydb/core/config/ut/ya.make @@ -0,0 +1,13 @@ +UNITTEST() + +SRCS( + main.cpp +) + +PEERDIR( + ydb/core/config/utils + library/cpp/colorizer + library/cpp/testing/unittest +) + +END() diff --git a/ydb/core/config/utils/config_traverse.cpp b/ydb/core/config/utils/config_traverse.cpp new file mode 100644 index 000000000000..0ef5eea0d5a9 --- /dev/null +++ b/ydb/core/config/utils/config_traverse.cpp @@ -0,0 +1,59 @@ +#include "config_traverse.h" + +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace NKikimr::NConfig { + +ssize_t FindLoop(TDeque& typePath, const Descriptor* child) { + for (ssize_t i = 0; i < (ssize_t)typePath.size(); ++i) { + if (typePath[i] == child) { + return i; + } + } + return -1; +} + +void Traverse(const Descriptor* d, TDeque& typePath, TDeque& fieldPath, const FieldDescriptor* field, TOnEntryFn onEntry) { + ssize_t loop = FindLoop(typePath, d); + + Y_ABORT_IF(!d && !field, "Either field or descriptor must be defined"); + + onEntry(d, typePath, fieldPath, field, loop); + + if (!d || loop != -1) { + return; + } + + typePath.push_back(d); + + for (int i = 0; i < d->field_count(); ++i) { + const FieldDescriptor* fieldDescriptor = d->field(i); + fieldPath.push_back(fieldDescriptor); + Traverse(fieldDescriptor->message_type(), typePath, fieldPath, fieldDescriptor, onEntry); + fieldPath.pop_back(); + } + + typePath.pop_back(); +} + +void Traverse(TOnEntryFn onEntry) { + auto& inst = NKikimrConfig::TAppConfig::default_instance(); + const Descriptor* descriptor = inst.GetDescriptor(); + + TDeque typePath; + TDeque fieldPath; + fieldPath.push_back(nullptr); + Traverse(descriptor, typePath, fieldPath, nullptr, onEntry); + fieldPath.pop_back(); +} + +} // namespace NKikimr::NConfig diff --git a/ydb/core/config/utils/config_traverse.h b/ydb/core/config/utils/config_traverse.h new file mode 100644 index 000000000000..7b0468f4b3a5 --- /dev/null +++ b/ydb/core/config/utils/config_traverse.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include + +namespace google::protobuf { + class Descriptor; + class FieldDescriptor; +} + +namespace NKikimr::NConfig { + +using namespace google::protobuf; + +using TOnEntryFn = std::function&, const TDeque&, const FieldDescriptor*, ssize_t)>; + +void Traverse(TOnEntryFn onEntry); + +} // namespace NKikimr::NConfig diff --git a/ydb/core/config/utils/ya.make b/ydb/core/config/utils/ya.make new file mode 100644 index 000000000000..bd7b82fcc9e4 --- /dev/null +++ b/ydb/core/config/utils/ya.make @@ -0,0 +1,12 @@ +LIBRARY() + +SRCS( + config_traverse.cpp +) + +PEERDIR( + ydb/core/protos + library/cpp/protobuf/json +) + +END() diff --git a/ydb/core/config/ya.make b/ydb/core/config/ya.make new file mode 100644 index 000000000000..06efc6d2a17c --- /dev/null +++ b/ydb/core/config/ya.make @@ -0,0 +1,7 @@ +RECURSE( + utils +) + +RECURSE_FOR_TESTS( + ut +) diff --git a/ydb/core/ya.make b/ydb/core/ya.make index 448497f2b4cd..acdeb243630f 100644 --- a/ydb/core/ya.make +++ b/ydb/core/ya.make @@ -8,6 +8,7 @@ RECURSE( client cms control + config debug debug_tools discovery