|
1 | 1 | #include "distconf_invoke.h" |
2 | 2 | #include "node_warden_impl.h" |
3 | 3 |
|
| 4 | +#include <ydb/core/config/validation/validators.h> |
4 | 5 | #include <ydb/core/mind/bscontroller/bsc.h> |
5 | 6 |
|
| 7 | +#include <ydb/library/yaml_config/yaml_config.h> |
6 | 8 | #include <ydb/library/yaml_config/yaml_config_parser.h> |
7 | 9 | #include <ydb/library/yaml_json/yaml_to_json.h> |
8 | 10 |
|
9 | 11 | namespace NKikimr::NStorage { |
10 | 12 |
|
11 | 13 | using TInvokeRequestHandlerActor = TDistributedConfigKeeper::TInvokeRequestHandlerActor; |
12 | 14 |
|
| 15 | + namespace { |
| 16 | + static std::optional<TString> LocalYamlValidate(const TString& yaml, bool allowUnknown = true) { |
| 17 | + try { |
| 18 | + auto doc = NFyaml::TDocument::Parse(yaml); |
| 19 | + auto resolved = NYamlConfig::ResolveAll(doc); |
| 20 | + |
| 21 | + TSimpleSharedPtr<NYamlConfig::TBasicUnknownFieldsCollector> unknownCollector = |
| 22 | + new NYamlConfig::TBasicUnknownFieldsCollector; |
| 23 | + |
| 24 | + std::vector<TString> errors; |
| 25 | + for (auto& [_, config] : resolved.Configs) { |
| 26 | + auto appCfg = NYamlConfig::YamlToProto( |
| 27 | + config.second, |
| 28 | + true, // strict |
| 29 | + true, // merge database config (if any) |
| 30 | + unknownCollector); |
| 31 | + if (NKikimr::NConfig::ValidateConfig(appCfg, errors) == NKikimr::NConfig::EValidationResult::Error) { |
| 32 | + if (!errors.empty()) { |
| 33 | + return errors.front(); |
| 34 | + } else { |
| 35 | + return TString("unknown validation error"); |
| 36 | + } |
| 37 | + } |
| 38 | + } |
| 39 | + |
| 40 | + if (!allowUnknown && !unknownCollector->GetUnknownKeys().empty()) { |
| 41 | + return TString("has forbidden unknown fields"); |
| 42 | + } |
| 43 | + |
| 44 | + return std::nullopt; |
| 45 | + } catch (const std::exception& ex) { |
| 46 | + return TString(ex.what()); |
| 47 | + } |
| 48 | + } |
| 49 | + } |
| 50 | + |
13 | 51 | void TInvokeRequestHandlerActor::FetchStorageConfig(bool manual, bool fetchMain, bool fetchStorage) { |
14 | 52 | if (!Self->StorageConfig) { |
15 | 53 | FinishWithError(TResult::ERROR, "no agreed StorageConfig"); |
@@ -484,7 +522,15 @@ namespace NKikimr::NStorage { |
484 | 522 | Y_ABORT_UNLESS(prevState == ERootState::IN_PROGRESS); |
485 | 523 | return error; |
486 | 524 | }, |
487 | | - [&](NKikimrBlobStorage::TStorageConfig& proposedConfig) { |
| 525 | + [&](NKikimrBlobStorage::TStorageConfig& proposedConfig) -> std::optional<TString> { |
| 526 | + // config validation, which is basically done in Console |
| 527 | + TString mainYaml; |
| 528 | + if (auto err = DecomposeConfig(proposedConfig.GetConfigComposite(), &mainYaml, /*mainConfigVersion=*/ nullptr, /*mainConfigFetchYaml=*/ nullptr)) { |
| 529 | + return TString(TStringBuilder() << "Failed to decompose composite config: " << *err); |
| 530 | + } |
| 531 | + if (auto err = LocalYamlValidate(mainYaml, /*allowUnknown=*/ true)) { |
| 532 | + return TString(TStringBuilder() << "YAML validation failed: " << *err); |
| 533 | + } |
488 | 534 | StartProposition(&proposedConfig, false); |
489 | 535 | return std::nullopt; |
490 | 536 | } |
|
0 commit comments