Skip to content

Commit 5f155ea

Browse files
mregrockblinkov
authored andcommitted
Fix fetch and replace for database config (#15253)
1 parent 3bce5b0 commit 5f155ea

File tree

7 files changed

+315
-39
lines changed

7 files changed

+315
-39
lines changed

ydb/apps/ydb/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
* Added support for dual configuration mode in the `ydb admin cluster config fetch` command, allowing it to handle separate cluster and storage config sections.
12
* Add options for client certificates in SSL/TLS connections.
23
* Add `ydb admin node config init` command to initialize directory with node config files.
34
* Add `ydb admin cluster config generate` command to generate dynamic config from static config on cluster.

ydb/core/cms/console/console_configs_manager.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,24 @@ class TConfigsManager : public TActorBootstrapped<TConfigsManager> {
216216
HandleUnauthorized(ev, ctx);
217217
};
218218

219+
constexpr bool HasBypassAuth = std::is_same_v<
220+
std::decay_t<T>,
221+
typename TEvConsole::TEvGetAllConfigsRequest::TPtr
222+
> || std::is_same_v<
223+
std::decay_t<T>,
224+
typename TEvConsole::TEvReplaceYamlConfigRequest::TPtr
225+
> || std::is_same_v<
226+
std::decay_t<T>,
227+
typename TEvConsole::TEvSetYamlConfigRequest::TPtr
228+
>;
229+
230+
if constexpr (HasBypassAuth) {
231+
if (ev->Get()->Record.HasBypassAuth() && ev->Get()->Record.GetBypassAuth()) {
232+
Handle(ev, ctx);
233+
return;
234+
}
235+
}
236+
219237
if (IsAdministrator(AppData(ctx), ev->Get()->Record.GetUserToken())) {
220238
Handle(ev, ctx);
221239
} else {

ydb/core/grpc_services/rpc_config.cpp

Lines changed: 193 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#include <ydb/core/protos/local.pb.h>
1212
#include <ydb/core/blobstorage/nodewarden/node_warden_events.h>
1313
#include <ydb/core/base/auth.h>
14+
#include <ydb/core/cms/console/console.h>
15+
#include <ydb/core/cms/console/configs_dispatcher.h>
1416

1517
namespace NKikimr::NGRpcService {
1618

@@ -136,10 +138,28 @@ void CopyFromConfigResponse(const NKikimrBlobStorage::TConfigResponse &from, Ydb
136138

137139
class TReplaceStorageConfigRequest : public TBSConfigRequestGrpc<TReplaceStorageConfigRequest, TEvReplaceStorageConfigRequest,
138140
Ydb::Config::ReplaceConfigResult> {
139-
public:
140141
using TBase = TBSConfigRequestGrpc<TReplaceStorageConfigRequest, TEvReplaceStorageConfigRequest, Ydb::Config::ReplaceConfigResult>;
142+
using TRpcBase = TRpcOperationRequestActor<TReplaceStorageConfigRequest, TEvReplaceStorageConfigRequest>;
143+
public:
141144
using TBase::TBase;
142145

146+
void Bootstrap(const TActorContext& ctx) {
147+
TRpcBase::Bootstrap(ctx);
148+
auto *self = Self();
149+
self->OnBootstrap();
150+
const auto& request = *GetProtoRequest();
151+
auto shim = ConvertConfigReplaceRequest(request);
152+
if (shim.MainConfig) {
153+
if (NYamlConfig::IsDatabaseConfig(*shim.MainConfig)) {
154+
DatabaseConfig = shim.MainConfig;
155+
CheckDatabaseAuthorization();
156+
return;
157+
}
158+
}
159+
self->Become(&TReplaceStorageConfigRequest::StateFunc);
160+
self->Send(MakeBlobStorageNodeWardenID(ctx.SelfID.NodeId()), new TEvNodeWardenQueryStorageConfig(false));
161+
}
162+
143163
bool ValidateRequest(Ydb::StatusIds::StatusCode& status, NYql::TIssues& issues) override {
144164
const auto& request = *GetProtoRequest();
145165
if (request.dry_run()) {
@@ -207,13 +227,137 @@ class TReplaceStorageConfigRequest : public TBSConfigRequestGrpc<TReplaceStorage
207227
request->allow_unknown_fields() || request->bypass_checks(),
208228
request->bypass_checks());
209229
}
230+
231+
private:
232+
std::optional<TString> DatabaseConfig;
233+
std::optional<TString> TargetDatabase;
234+
235+
void CheckDatabaseAuthorization() {
236+
const auto& metadata = NYamlConfig::GetDatabaseMetadata(*DatabaseConfig);
237+
238+
if (metadata.Database) {
239+
TargetDatabase = metadata.Database;
240+
}
241+
else {
242+
Reply(Ydb::StatusIds::BAD_REQUEST, "No database name found in metadata",
243+
NKikimrIssues::TIssuesIds::DEFAULT_ERROR, ActorContext());
244+
return;
245+
}
246+
247+
if (*TargetDatabase == ("/" + AppData()->DomainsInfo->Domain->Name) ||
248+
*TargetDatabase == AppData()->DomainsInfo->Domain->Name) {
249+
Reply(Ydb::StatusIds::BAD_REQUEST, "Provided database is a domain database.",
250+
NKikimrIssues::TIssuesIds::DEFAULT_ERROR, ActorContext());
251+
return;
252+
}
253+
bool isAdministrator = NKikimr::IsAdministrator(AppData(), Request_->GetSerializedToken());
254+
if (!isAdministrator) {
255+
auto request = std::make_unique<NSchemeCache::TSchemeCacheNavigate>();
256+
request->DatabaseName = *TargetDatabase;
257+
258+
auto& entry = request->ResultSet.emplace_back();
259+
entry.Operation = NSchemeCache::TSchemeCacheNavigate::OpPath;
260+
entry.Path = NKikimr::SplitPath(*TargetDatabase);
261+
262+
auto* self = Self();
263+
self->Send(MakeSchemeCacheID(), new TEvTxProxySchemeCache::TEvNavigateKeySet(request.release()));
264+
self->Become(&TReplaceStorageConfigRequest::StateWaitResolveDatabase);
265+
return;
266+
}
267+
SendRequestToConsole();
268+
}
269+
270+
void SendRequestToConsole() {
271+
NTabletPipe::TClientConfig pipeConfig;
272+
pipeConfig.RetryPolicy = {
273+
.RetryLimitCount = 10,
274+
};
275+
auto pipe = NTabletPipe::CreateClient(SelfId(), MakeConsoleID(), pipeConfig);
276+
ConsolePipe = RegisterWithSameMailbox(pipe);
277+
278+
auto PrepareAndSendRequest = [&](auto requestType) {
279+
using TRequestType = decltype(requestType);
280+
auto request = std::make_unique<TRequestType>();
281+
request->Record.SetUserToken(Request_->GetSerializedToken());
282+
request->Record.SetPeerName(Request_->GetPeerName());
283+
request->Record.SetIngressDatabase(*TargetDatabase);
284+
285+
auto& req = *request->Record.MutableRequest();
286+
req.set_config(*DatabaseConfig);
287+
288+
request->Record.SetBypassAuth(true);
289+
NTabletPipe::SendData(SelfId(), ConsolePipe, request.release());
290+
};
291+
292+
if (GetProtoRequest()->bypass_checks()) {
293+
PrepareAndSendRequest(NConsole::TEvConsole::TEvSetYamlConfigRequest());
294+
} else {
295+
PrepareAndSendRequest(NConsole::TEvConsole::TEvReplaceYamlConfigRequest());
296+
}
297+
Self()->Become(&TReplaceStorageConfigRequest::StateConsoleReplaceFunc);
298+
}
299+
300+
STFUNC(StateWaitResolveDatabase) {
301+
switch (ev->GetTypeRewrite()) {
302+
hFunc(TEvTxProxySchemeCache::TEvNavigateKeySetResult, HandleResolveDatabase);
303+
default:
304+
return TBase::StateFuncBase(ev);
305+
}
306+
}
307+
308+
void HandleResolveDatabase(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev) {
309+
const NSchemeCache::TSchemeCacheNavigate& request = *ev->Get()->Request.Get();
310+
auto *self = Self();
311+
if (request.ResultSet.empty() || request.ErrorCount > 0) {
312+
self->Reply(Ydb::StatusIds::SCHEME_ERROR, "Error resolving database",
313+
NKikimrIssues::TIssuesIds::GENERIC_RESOLVE_ERROR, self->ActorContext());
314+
return;
315+
}
316+
317+
const auto& entry = request.ResultSet.front();
318+
const auto& databaseOwner = entry.Self->Info.GetOwner();
319+
320+
NACLibProto::TUserToken tokenPb;
321+
if (!tokenPb.ParseFromString(Request_->GetSerializedToken())) {
322+
tokenPb = NACLibProto::TUserToken();
323+
}
324+
const auto& parsedToken = NACLib::TUserToken(tokenPb);
325+
326+
bool isDatabaseAdmin = NKikimr::IsDatabaseAdministrator(&parsedToken, databaseOwner);
327+
if (!isDatabaseAdmin) {
328+
self->Reply(Ydb::StatusIds::UNAUTHORIZED, "User is not a database administrator.",
329+
NKikimrIssues::TIssuesIds::ACCESS_DENIED, self->ActorContext());
330+
return;
331+
}
332+
SendRequestToConsole();
333+
}
334+
335+
STFUNC(StateConsoleReplaceFunc) {
336+
switch (ev->GetTypeRewrite()) {
337+
hFunc(NConsole::TEvConsole::TEvReplaceYamlConfigResponse, Handle);
338+
hFunc(NConsole::TEvConsole::TEvSetYamlConfigResponse, Handle);
339+
default:
340+
return StateConsoleFunc(ev);
341+
}
342+
}
343+
344+
void Handle(NConsole::TEvConsole::TEvReplaceYamlConfigResponse::TPtr& ev) {
345+
auto* self = Self();
346+
self->Reply(Ydb::StatusIds::SUCCESS, ev->Get()->Record.GetIssues(), self->ActorContext());
347+
}
348+
349+
void Handle(NConsole::TEvConsole::TEvSetYamlConfigResponse::TPtr& ev) {
350+
auto* self = Self();
351+
self->Reply(Ydb::StatusIds::SUCCESS, ev->Get()->Record.GetIssues(), self->ActorContext());
352+
}
210353
};
211354

212355
class TFetchStorageConfigRequest : public TBSConfigRequestGrpc<TFetchStorageConfigRequest, TEvFetchStorageConfigRequest,
213356
Ydb::Config::FetchConfigResult> {
214357
public:
215358
using TBase = TBSConfigRequestGrpc<TFetchStorageConfigRequest, TEvFetchStorageConfigRequest, Ydb::Config::FetchConfigResult>;
216359
using TBase::TBase;
360+
using TRpcBase = TRpcOperationRequestActor<TFetchStorageConfigRequest, TEvFetchStorageConfigRequest>;
217361

218362
bool ValidateRequest(Ydb::StatusIds::StatusCode& status, NYql::TIssues& issues) override {
219363
const auto& request = *GetProtoRequest();
@@ -229,6 +373,20 @@ class TFetchStorageConfigRequest : public TBSConfigRequestGrpc<TFetchStorageConf
229373
return NACLib::GenericManage;
230374
}
231375

376+
void Bootstrap(const TActorContext &ctx) {
377+
TRpcBase::Bootstrap(ctx);
378+
auto *self = Self();
379+
self->OnBootstrap();
380+
381+
if (self->Request_->GetDatabaseName()) {
382+
SendRequestToConsole();
383+
return;
384+
}
385+
386+
self->Become(&TFetchStorageConfigRequest::StateFunc);
387+
self->Send(MakeBlobStorageNodeWardenID(ctx.SelfID.NodeId()), new TEvNodeWardenQueryStorageConfig(false));
388+
}
389+
232390
void FillDistconfQuery(NStorage::TEvNodeConfigInvokeOnRoot& ev) const {
233391
auto *record = ev.Record.MutableFetchStorageConfig();
234392

@@ -300,6 +458,39 @@ class TFetchStorageConfigRequest : public TBSConfigRequestGrpc<TFetchStorageConf
300458

301459
return ev;
302460
}
461+
462+
private:
463+
void SendRequestToConsole() {
464+
NTabletPipe::TClientConfig pipeConfig;
465+
pipeConfig.RetryPolicy = {
466+
.RetryLimitCount = 10,
467+
};
468+
auto pipe = NTabletPipe::CreateClient(SelfId(), MakeConsoleID(), pipeConfig);
469+
ConsolePipe = RegisterWithSameMailbox(pipe);
470+
471+
auto request = std::make_unique<NConsole::TEvConsole::TEvGetAllConfigsRequest>();
472+
request->Record.SetUserToken(Request_->GetSerializedToken());
473+
request->Record.SetPeerName(Request_->GetPeerName());
474+
if (Request_->GetDatabaseName()) {
475+
request->Record.SetIngressDatabase(*Request_->GetDatabaseName());
476+
}
477+
request->Record.SetBypassAuth(true);
478+
479+
NTabletPipe::SendData(SelfId(), ConsolePipe, request.release());
480+
Self()->Become(&TFetchStorageConfigRequest::StateConsoleFetchFunc);
481+
}
482+
483+
STFUNC(StateConsoleFetchFunc) {
484+
switch (ev->GetTypeRewrite()) {
485+
hFunc(NConsole::TEvConsole::TEvGetAllConfigsResponse, Handle);
486+
default:
487+
return StateConsoleFunc(ev);
488+
}
489+
}
490+
491+
void Handle(NConsole::TEvConsole::TEvGetAllConfigsResponse::TPtr& ev) {
492+
ReplyWithResult(Ydb::StatusIds::SUCCESS, ev->Get()->Record.GetResponse(), ActorContext());
493+
}
303494
};
304495

305496
void DoReplaceConfig(std::unique_ptr<IRequestOpCtx> p, const IFacilityProvider&) {
@@ -372,3 +563,4 @@ void DoBootstrapCluster(std::unique_ptr<IRequestOpCtx> p, const IFacilityProvide
372563
}
373564

374565
} // namespace NKikimr::NGRpcService
566+

ydb/core/grpc_services/rpc_config_base.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <ydb/core/ydb_convert/ydb_convert.h>
1616
#include <ydb-cpp-sdk/library/operation_id/operation_id.h>
1717
#include <ydb-cpp-sdk/client/resources/ydb_resources.h>
18+
#include <ydb/core/cms/console/console.h>
1819

1920
namespace NKikimr::NGRpcService {
2021

@@ -351,6 +352,37 @@ class TBSConfigRequestGrpc : public TRpcOperationRequestActor<TDerived, TRequest
351352
NKikimrIssues::TIssuesIds::SHARD_NOT_AVAILABLE, self->ActorContext());
352353
}
353354

355+
TActorId ConsolePipe;
356+
357+
STFUNC(StateConsoleFunc) {
358+
switch (ev->GetTypeRewrite()) {
359+
hFunc(NConsole::TEvConsole::TEvGenericError, HandleConsole);
360+
hFunc(TEvTabletPipe::TEvClientDestroyed, HandleConsole);
361+
hFunc(TEvTabletPipe::TEvClientConnected, HandleConsole);
362+
default:
363+
return TBase::StateFuncBase(ev);
364+
}
365+
}
366+
367+
void HandleConsole(NConsole::TEvConsole::TEvGenericError::TPtr& ev) {
368+
auto *self = Self();
369+
self->Reply(ev->Get()->Record.GetYdbStatus(), ev->Get()->Record.GetIssues(), self->ActorContext());
370+
}
371+
372+
void HandleConsole(TEvTabletPipe::TEvClientDestroyed::TPtr&) {
373+
auto *self = Self();
374+
self->Reply(Ydb::StatusIds::UNAVAILABLE, "Connection to Console was lost",
375+
NKikimrIssues::TIssuesIds::SHARD_NOT_AVAILABLE, self->ActorContext());
376+
}
377+
378+
void HandleConsole(TEvTabletPipe::TEvClientConnected::TPtr& ev) {
379+
if (ev->Get()->Status != NKikimrProto::OK) {
380+
auto *self = Self();
381+
self->Reply(Ydb::StatusIds::UNAVAILABLE, "Failed to connect to Console",
382+
NKikimrIssues::TIssuesIds::SHARD_NOT_AVAILABLE, self->ActorContext());
383+
}
384+
}
385+
354386
virtual bool ValidateRequest(Ydb::StatusIds::StatusCode& status, NYql::TIssues& issues) = 0;
355387
virtual std::unique_ptr<IEventBase> ProcessControllerQuery() = 0;
356388

ydb/core/protos/console_config.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ message TSetYamlConfigRequest {
265265
optional bytes UserToken = 2;
266266
optional string PeerName = 3;
267267
optional string IngressDatabase = 4;
268+
optional bool BypassAuth = 5;
268269
}
269270

270271
message TSetYamlConfigResponse {
@@ -276,6 +277,7 @@ message TReplaceYamlConfigRequest {
276277
optional bytes UserToken = 2;
277278
optional string PeerName = 3;
278279
optional string IngressDatabase = 4;
280+
optional bool BypassAuth = 5;
279281
}
280282

281283
message TReplaceYamlConfigResponse {
@@ -307,6 +309,7 @@ message TGetAllConfigsRequest {
307309
optional bytes UserToken = 2;
308310
optional string PeerName = 3;
309311
optional string IngressDatabase = 4;
312+
optional bool BypassAuth = 5;
310313
}
311314

312315
message TGetAllConfigsResponse {

0 commit comments

Comments
 (0)