11#pragma once
22#include " json_pipe_req.h"
33#include " viewer.h"
4+ #include < ydb/library/yaml_config/yaml_config.h>
45
56namespace NKikimr ::NViewer {
67
@@ -11,132 +12,174 @@ class TJsonFeatureFlags : public TViewerPipeClient {
1112 using TBase = TViewerPipeClient;
1213 TJsonSettings JsonSettings;
1314 ui32 Timeout = 0 ;
14-
1515 TString FilterDatabase;
1616 THashSet<TString> FilterFeatures;
17- THashMap<ui64, TString> DatabaseByCookie;
18- ui64 Cookie = 0 ;
19- TString DomainPath;
20- bool Direct = false ;
21-
2217 TRequestResponse<NConsole::TEvConsole::TEvListTenantsResponse> TenantsResponse;
23- THashMap<TString, TRequestResponse<NConsole::TEvConsole::TEvGetNodeConfigResponse>> NodeConfigResponses;
18+ TRequestResponse<NConsole::TEvConsole::TEvGetAllConfigsResponse> AllConfigsResponse;
19+ std::unordered_map<TString, TRequestResponse<TEvTxProxySchemeCache::TEvNavigateKeySetResult>> PathNameNavigateKeySetResults;
20+ std::unordered_map<TPathId, TRequestResponse<TEvTxProxySchemeCache::TEvNavigateKeySetResult>> PathIdNavigateKeySetResults;
2421
2522public:
2623 TJsonFeatureFlags (IViewer* viewer, NMon::TEvHttpInfo::TPtr &ev)
2724 : TViewerPipeClient(viewer, ev)
2825 {}
2926
30- void MakeNodeConfigRequest (const TString& database) {
31- NodeConfigResponses[database] = MakeRequestConsoleNodeConfigByTenant (database, Cookie);
32- DatabaseByCookie[Cookie++] = database;
33- }
34-
3527 void Bootstrap () override {
3628 const auto & params (Event->Get ()->Request .GetParams ());
37-
3829 JsonSettings.EnumAsNumbers = !FromStringWithDefault<bool >(params.Get (" enums" ), true );
3930 JsonSettings.UI64AsString = !FromStringWithDefault<bool >(params.Get (" ui64" ), false );
4031 FilterDatabase = params.Get (" database" );
4132 StringSplitter (params.Get (" features" )).Split (' ,' ).SkipEmpty ().Collect (&FilterFeatures);
42- Direct = FromStringWithDefault<bool >(params.Get (" direct" ), Direct );
33+ bool direct = FromStringWithDefault<bool >(params.Get (" direct" ), false );
4334 Timeout = FromStringWithDefault<ui32>(params.Get (" timeout" ), 10000 );
4435
45- TIntrusivePtr<TDomainsInfo> domains = AppData ()->DomainsInfo ;
46- auto * domain = domains->GetDomain ();
47- DomainPath = " /" + domain->Name ;
36+ direct |= !TBase::Event->Get ()->Request .GetHeader (" X-Forwarded-From-Node" ).empty (); // we're already forwarding
37+ direct |= (FilterDatabase == AppData ()->TenantName ); // we're already on the right node
38+ if (FilterDatabase && !direct) {
39+ return RedirectToDatabase (FilterDatabase); // to find some dynamic node and redirect query there
40+ }
4841
49- Direct |= !TBase::Event->Get ()->Request .GetHeader (" X-Forwarded-From-Node" ).empty (); // we're already forwarding
50- Direct |= (FilterDatabase == AppData ()->TenantName ); // we're already on the right node
51- if (FilterDatabase && !Direct) {
52- RequestStateStorageEndpointsLookup (FilterDatabase); // to find some dynamic node and redirect there
53- } else if (!FilterDatabase) {
54- MakeNodeConfigRequest (DomainPath);
55- TenantsResponse = MakeRequestConsoleListTenants ();
42+ if (FilterDatabase) {
43+ PathNameNavigateKeySetResults[FilterDatabase] = MakeRequestSchemeCacheNavigate (FilterDatabase);
5644 } else {
57- MakeNodeConfigRequest (FilterDatabase );
45+ TenantsResponse = MakeRequestConsoleListTenants ( );
5846 }
47+ AllConfigsResponse = MakeRequestConsoleGetAllConfigs ();
5948
6049 Become (&TThis::StateWork, TDuration::MilliSeconds (Timeout), new TEvents::TEvWakeup ());
6150 }
6251
63- void HandleReply (TEvStateStorage::TEvBoardInfo::TPtr& ev) {
64- TBase::ReplyAndPassAway (MakeForward (GetNodesFromBoardReply (ev)));
65- }
66-
6752 STATEFN (StateWork) {
6853 switch (ev->GetTypeRewrite ()) {
69- hFunc (TEvStateStorage::TEvBoardInfo, HandleReply);
7054 hFunc (NConsole::TEvConsole::TEvListTenantsResponse, Handle);
71- hFunc (NConsole::TEvConsole::TEvGetNodeConfigResponse, Handle);
55+ hFunc (NConsole::TEvConsole::TEvGetAllConfigsResponse, Handle);
56+ hFunc (TEvTxProxySchemeCache::TEvNavigateKeySetResult, Handle);
7257 hFunc (TEvTabletPipe::TEvClientConnected, TBase::Handle);
7358 cFunc (TEvents::TSystem::Wakeup, HandleTimeout);
7459 }
7560 }
7661
7762 void Handle (NConsole::TEvConsole::TEvListTenantsResponse::TPtr& ev) {
7863 TenantsResponse.Set (std::move (ev));
79- Ydb::Cms::ListDatabasesResult listDatabasesResult;
80- TenantsResponse->Record .GetResponse ().operation ().result ().UnpackTo (&listDatabasesResult);
81- for (const TString& path : listDatabasesResult.paths ()) {
82- MakeNodeConfigRequest (path);
64+ if (TenantsResponse.IsOk ()) {
65+ Ydb::Cms::ListDatabasesResult listDatabasesResult;
66+ TenantsResponse->Record .GetResponse ().operation ().result ().UnpackTo (&listDatabasesResult);
67+ for (const TString& database : listDatabasesResult.paths ()) {
68+ if (PathNameNavigateKeySetResults.count (database) == 0 ) {
69+ PathNameNavigateKeySetResults[database] = MakeRequestSchemeCacheNavigate (database);
70+ }
71+ }
8372 }
8473 RequestDone ();
8574 }
8675
87- void Handle (NConsole::TEvConsole::TEvGetNodeConfigResponse::TPtr& ev) {
88- TString database = DatabaseByCookie[ev.Get ()->Cookie ];
89- NodeConfigResponses[database].Set (std::move (ev));
76+ void Handle (NConsole::TEvConsole::TEvGetAllConfigsResponse::TPtr& ev) {
77+ AllConfigsResponse.Set (std::move (ev));
9078 RequestDone ();
9179 }
9280
93- THashMap<TString, NKikimrViewer::TFeatureFlagsConfig::TFeatureFlag> ParseFeatureFlags (const NKikimrConfig::TFeatureFlags& featureFlags) {
94- THashMap<TString, NKikimrViewer::TFeatureFlagsConfig::TFeatureFlag> features;
81+ void Handle (TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev) {
82+ TString path = GetPath (ev);
83+ if (path) {
84+ auto it = PathNameNavigateKeySetResults.find (path);
85+ if (it != PathNameNavigateKeySetResults.end () && !it->second .IsDone ()) {
86+ it->second .Set (std::move (ev));
87+ if (it->second .IsOk ()) {
88+ TSchemeCacheNavigate::TEntry& entry (it->second ->Request ->ResultSet .front ());
89+ if (entry.DomainInfo ) {
90+ if (entry.DomainInfo ->ResourcesDomainKey && entry.DomainInfo ->DomainKey != entry.DomainInfo ->ResourcesDomainKey ) {
91+ TPathId resourceDomainKey (entry.DomainInfo ->ResourcesDomainKey );
92+ if (PathIdNavigateKeySetResults.count (resourceDomainKey) == 0 ) {
93+ PathIdNavigateKeySetResults[resourceDomainKey] = MakeRequestSchemeCacheNavigate (resourceDomainKey);
94+ }
95+ }
96+ }
97+ }
98+ RequestDone ();
99+ return ;
100+ }
101+ }
102+ TPathId pathId = GetPathId (ev);
103+ if (pathId) {
104+ auto it = PathIdNavigateKeySetResults.find (pathId);
105+ if (it != PathIdNavigateKeySetResults.end () && !it->second .IsDone ()) {
106+ it->second .Set (std::move (ev));
107+ RequestDone ();
108+ return ;
109+ }
110+ }
111+ }
112+
113+ void ParseFeatureFlags (const NKikimrConfig::TFeatureFlags& featureFlags, NKikimrViewer::TFeatureFlagsConfig::TDatabase& result) {
95114 const google::protobuf::Reflection* reflection = featureFlags.GetReflection ();
96115 const google::protobuf::Descriptor* descriptor = featureFlags.GetDescriptor ();
97-
98116 for (int i = 0 ; i < descriptor->field_count (); ++i) {
99117 const google::protobuf::FieldDescriptor* field = descriptor->field (i);
100118 if (field->cpp_type () == google::protobuf::FieldDescriptor::CPPTYPE_BOOL) {
101- auto & feat = features[field->name ()];
102- feat.SetName (field->name ());
103- if (reflection->HasField (featureFlags, field)) {
104- feat.SetCurrent (reflection->GetBool (featureFlags, field));
105- }
106- if (field->has_default_value ()) {
107- feat.SetDefault (field->default_value_bool ());
119+ if (FilterFeatures.empty () || FilterFeatures.count (field->name ())) {
120+ auto flag = result.AddFeatureFlags ();
121+ flag->SetName (field->name ());
122+ if (reflection->HasField (featureFlags, field)) {
123+ flag->SetCurrent (reflection->GetBool (featureFlags, field));
124+ }
125+ if (field->has_default_value ()) {
126+ flag->SetDefault (field->default_value_bool ());
127+ }
108128 }
109129 }
110130 }
111- return features;
112131 }
113132
114- void ReplyAndPassAway () override {
115- THashMap<TString, THashMap<TString, NKikimrViewer::TFeatureFlagsConfig::TFeatureFlag>> FeatureFlagsByDatabase;
116- for (const auto & [database, response] : NodeConfigResponses) {
117- NKikimrConsole::TGetNodeConfigResponse rec = response->Record ;
118- FeatureFlagsByDatabase[database] = ParseFeatureFlags (rec.GetConfig ().GetFeatureFlags ());
119- }
120-
121- auto domainFeaturesIt = FeatureFlagsByDatabase.find (DomainPath);
122- if (domainFeaturesIt == FeatureFlagsByDatabase.end ()) {
123- return TBase::ReplyAndPassAway (GetHTTPINTERNALERROR (" text/plain" , " No domain info from Console" ));
133+ void ParseConfig (const TString& database, NKikimrViewer::TFeatureFlagsConfig& result) {
134+ if (AllConfigsResponse.IsOk ()) {
135+ TString realDatabase = database;
136+ NKikimrViewer::TFeatureFlagsConfig_TDatabase* databaseProto = nullptr ;
137+ auto itNavigate = PathNameNavigateKeySetResults.find (database);
138+ if (itNavigate != PathNameNavigateKeySetResults.end () && itNavigate->second .IsOk ()) {
139+ databaseProto = result.AddDatabases ();
140+ databaseProto->SetName (database);
141+ TSchemeCacheNavigate::TEntry& entry (itNavigate->second ->Request ->ResultSet .front ());
142+ if (entry.DomainInfo ) {
143+ if (entry.DomainInfo ->ResourcesDomainKey && entry.DomainInfo ->DomainKey != entry.DomainInfo ->ResourcesDomainKey ) {
144+ TPathId resourceDomainKey (entry.DomainInfo ->ResourcesDomainKey );
145+ auto it = PathIdNavigateKeySetResults.find (resourceDomainKey);
146+ if (it != PathIdNavigateKeySetResults.end () && it->second .IsOk () && it->second ->Request ->ResultSet .size () == 1 ) {
147+ realDatabase = CanonizePath (it->second ->Request ->ResultSet .begin ()->Path );
148+ }
149+ }
150+ }
151+ } else {
152+ return ;
153+ }
154+ NKikimrConfig::TAppConfig appConfig;
155+ if (AllConfigsResponse->Record .GetResponse ().config ()) {
156+ try {
157+ NYamlConfig::ResolveAndParseYamlConfig (AllConfigsResponse->Record .GetResponse ().config (), {}, {{" tenant" , realDatabase}}, appConfig);
158+ } catch (const std::exception& e) {
159+ BLOG_ERROR (" Failed to parse config for tenant " << realDatabase << " : " << e.what ());
160+ }
161+ ParseFeatureFlags (appConfig.GetFeatureFlags (), *databaseProto);
162+ } else {
163+ ParseFeatureFlags (AppData ()->FeatureFlags , *databaseProto);
164+ }
124165 }
166+ }
125167
168+ void ReplyAndPassAway () override {
126169 // prepare response
127170 NKikimrViewer::TFeatureFlagsConfig Result;
128171 Result.SetVersion (Viewer->GetCapabilityVersion (" /viewer/feature_flags" ));
129- for (const auto & [database, features] : FeatureFlagsByDatabase) {
130- auto databaseProto = Result.AddDatabases ();
131- databaseProto->SetName (database);
132- for (const auto & [name, featProto] : features) {
133- if (FilterFeatures.empty () || FilterFeatures.find (name) != FilterFeatures.end ()) {
134- auto flag = databaseProto->AddFeatureFlags ();
135- flag->CopyFrom (featProto);
172+ if (FilterDatabase) {
173+ ParseConfig (FilterDatabase, Result);
174+ } else {
175+ if (TenantsResponse.IsOk ()) {
176+ Ydb::Cms::ListDatabasesResult listDatabasesResult;
177+ TenantsResponse->Record .GetResponse ().operation ().result ().UnpackTo (&listDatabasesResult);
178+ for (const TString& database : listDatabasesResult.paths ()) {
179+ ParseConfig (database, Result);
136180 }
137181 }
138182 }
139-
140183 TStringStream json;
141184 TProtoToJson::ProtoToJson (json, Result, JsonSettings);
142185 TBase::ReplyAndPassAway (GetHTTPOKJSON (json.Str ()));
@@ -147,12 +190,13 @@ class TJsonFeatureFlags : public TViewerPipeClient {
147190 .Method = " get" ,
148191 .Tag = " viewer" ,
149192 .Summary = " Feature flags" ,
150- .Description = " Returns feature flags of each database"
193+ .Description = " Returns feature flags of a database"
151194 });
152195 yaml.AddParameter ({
153196 .Name = " database" ,
154197 .Description = " database name" ,
155198 .Type = " string" ,
199+ .Required = true ,
156200 });
157201 yaml.AddParameter ({
158202 .Name = " features" ,
@@ -169,16 +213,6 @@ class TJsonFeatureFlags : public TViewerPipeClient {
169213 .Description = " timeout in ms" ,
170214 .Type = " integer" ,
171215 });
172- yaml.AddParameter ({
173- .Name = " enums" ,
174- .Description = " convert enums to strings" ,
175- .Type = " boolean" ,
176- });
177- yaml.AddParameter ({
178- .Name = " ui64" ,
179- .Description = " return ui64 as number" ,
180- .Type = " boolean" ,
181- });
182216 yaml.SetResponseSchema (TProtoToYaml::ProtoToYamlSchema<NKikimrViewer::TFeatureFlagsConfig>());
183217 return yaml;
184218 }
0 commit comments