Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Snapshot Restore] Migrate to new ES client #95499

Merged
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,24 @@
* 2.0.
*/

import type { ElasticsearchClient } from 'src/core/server';

// Cloud has its own system for managing SLM policies and we want to make
// this clear when Snapshot and Restore is used in a Cloud deployment.
// Retrieve the Cloud-managed policies so that UI can switch
// logical paths based on this information.
export const getManagedPolicyNames = async (callWithInternalUser: any): Promise<string[]> => {
export const getManagedPolicyNames = async (
clusterClient: ElasticsearchClient
): Promise<string[]> => {
try {
const { persistent, transient, defaults } = await callWithInternalUser('cluster.getSettings', {
filterPath: '*.*managed_policies',
flatSettings: true,
includeDefaults: true,
const {
body: { persistent, transient, defaults },
} = await clusterClient.cluster.getSettings({
filter_path: '*.*managed_policies',
flat_settings: true,
include_defaults: true,
});

const { 'cluster.metadata.managed_policies': managedPolicyNames = [] } = {
...defaults,
...persistent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,22 @@
* 2.0.
*/

import type { ElasticsearchClient } from 'src/core/server';

// Cloud has its own system for managing snapshots and we want to make
// this clear when Snapshot and Restore is used in a Cloud deployment.
// Retrieve the Cloud-managed repository name so that UI can switch
// logical paths based on this information.
export const getManagedRepositoryName = async (
callWithInternalUser: any
client: ElasticsearchClient
): Promise<string | undefined> => {
try {
const { persistent, transient, defaults } = await callWithInternalUser('cluster.getSettings', {
filterPath: '*.*managed_repository',
flatSettings: true,
includeDefaults: true,
const {
body: { persistent, transient, defaults },
} = await client.cluster.getSettings({
filter_path: '*.*managed_repository',
flat_settings: true,
include_defaults: true,
});
const { 'cluster.metadata.managed_repository': managedRepositoryName = undefined } = {
...defaults,
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/snapshot_restore/server/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ export { getManagedRepositoryName } from './get_managed_repository_name';
export { getManagedPolicyNames } from './get_managed_policy_names';
export { deserializeRestoreShard } from './restore_serialization';
export { wrapEsError } from './wrap_es_error';
export { isEsError } from './is_es_error';
8 changes: 8 additions & 0 deletions x-pack/plugins/snapshot_restore/server/lib/is_es_error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export const isEsError = (e: Error) => e.name === 'ResponseError';
27 changes: 5 additions & 22 deletions x-pack/plugins/snapshot_restore/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,11 @@ import {
import { PLUGIN, APP_REQUIRED_CLUSTER_PRIVILEGES } from '../common';
import { License } from './services';
import { ApiRoutes } from './routes';
import { wrapEsError } from './lib';
import { isEsError } from './shared_imports';
import { elasticsearchJsPlugin } from './client/elasticsearch_sr';
import type { Dependencies, SnapshotRestoreRequestHandlerContext } from './types';
import { wrapEsError, isEsError } from './lib';
import { handleEsError } from './shared_imports';
import type { Dependencies } from './types';
import { SnapshotRestoreConfig } from './config';

async function getCustomEsClient(getStartServices: CoreSetup['getStartServices']) {
const [core] = await getStartServices();
const esClientConfig = { plugins: [elasticsearchJsPlugin] };
return core.elasticsearch.legacy.createClient('snapshotRestore', esClientConfig);
}

export class SnapshotRestoreServerPlugin implements Plugin<void, void, any, any> {
private readonly logger: Logger;
private readonly apiRoutes: ApiRoutes;
Expand All @@ -52,7 +45,7 @@ export class SnapshotRestoreServerPlugin implements Plugin<void, void, any, any>
return;
}

const router = http.createRouter<SnapshotRestoreRequestHandlerContext>();
const router = http.createRouter();

this.license.setup(
{
Expand Down Expand Up @@ -82,17 +75,6 @@ export class SnapshotRestoreServerPlugin implements Plugin<void, void, any, any>
],
});

http.registerRouteHandlerContext<SnapshotRestoreRequestHandlerContext, 'snapshotRestore'>(
'snapshotRestore',
async (ctx, request) => {
this.snapshotRestoreESClient =
this.snapshotRestoreESClient ?? (await getCustomEsClient(getStartServices));
return {
client: this.snapshotRestoreESClient.asScoped(request),
};
}
);

this.apiRoutes.setup({
router,
license: this.license,
Expand All @@ -103,6 +85,7 @@ export class SnapshotRestoreServerPlugin implements Plugin<void, void, any, any>
},
lib: {
isEsError,
handleEsError,
wrapEsError,
},
});
Expand Down
55 changes: 23 additions & 32 deletions x-pack/plugins/snapshot_restore/server/routes/api/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ export function registerAppRoutes({
router,
config: { isSecurityEnabled },
license,
lib: { isEsError },
lib: { isEsError, handleEsError },
}: RouteDependencies) {
router.get(
{ path: addBasePath('privileges'), validate: false },
license.guardApiRoute(async (ctx, req, res) => {
const { callAsCurrentUser } = ctx.snapshotRestore!.client;
const { client: clusterClient } = ctx.core.elasticsearch;

const privilegesResult: Privileges = {
hasAllPrivileges: true,
Expand All @@ -48,42 +48,36 @@ export function registerAppRoutes({
}

try {
// Get cluster priviliges
const { has_all_requested: hasAllPrivileges, cluster } = await callAsCurrentUser(
'transport.request',
{
path: '/_security/user/_has_privileges',
method: 'POST',
body: {
cluster: [...APP_REQUIRED_CLUSTER_PRIVILEGES, ...APP_SLM_CLUSTER_PRIVILEGES],
},
}
);
// Get cluster privileges
const {
body: { has_all_requested: hasAllPrivileges, cluster },
} = await clusterClient.asCurrentUser.security.hasPrivileges({
body: {
cluster: [...APP_REQUIRED_CLUSTER_PRIVILEGES, ...APP_SLM_CLUSTER_PRIVILEGES],
},
});

// Find missing cluster privileges and set overall app privileges
privilegesResult.missingPrivileges.cluster = extractMissingPrivileges(cluster);
privilegesResult.hasAllPrivileges = hasAllPrivileges;

// Get all index privileges the user has
const { indices } = await callAsCurrentUser('transport.request', {
path: '/_security/user/_privileges',
method: 'GET',
});
const {
body: { indices },
} = await clusterClient.asCurrentUser.security.getUserPrivileges();

// Check if they have all the required index privileges for at least one index
const oneIndexWithAllPrivileges = indices.find(
({ privileges }: { privileges: string[] }) => {
if (privileges.includes('all')) {
return true;
}
const oneIndexWithAllPrivileges = indices.find(({ privileges }) => {
if (privileges.includes('all')) {
return true;
}

const indexHasAllPrivileges = APP_RESTORE_INDEX_PRIVILEGES.every((privilege) =>
privileges.includes(privilege)
);
const indexHasAllPrivileges = APP_RESTORE_INDEX_PRIVILEGES.every((privilege) =>
privileges.includes(privilege)
);

return indexHasAllPrivileges;
}
);
return indexHasAllPrivileges;
});

// If they don't, return list of required index privileges
if (!oneIndexWithAllPrivileges) {
Expand All @@ -93,10 +87,7 @@ export function registerAppRoutes({
return res.ok({ body: privilegesResult });
} catch (e) {
if (isEsError(e)) {
return res.customError({
statusCode: e.statusCode,
body: e,
});
return handleEsError({ error: e, response: res });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need the isEsError check on line 89; handleEsError should handle that for you, and throw if not.

https://github.com/elastic/kibana/blob/master/src/plugins/es_ui_shared/__packages_do_not_import__/errors/handle_es_error.ts

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right! I'll simplify the error handling a little bit!

}
// Case: default
throw e;
Expand Down
Loading