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

[Deprecations] Logs Sources settings in all spaces #203042

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0f800e6
[Deprecations] Logs Sources settings in all spaces
afharo Dec 5, 2024
4721ac5
Add TSDocs
afharo Dec 5, 2024
08c00be
Fix i18n labels
afharo Dec 5, 2024
61489d8
Fix types (mocks)
afharo Dec 5, 2024
08bf159
Fix types (mocks)
afharo Dec 5, 2024
79969b2
[Deprecations] Logs Sources settings in all spaces
afharo Dec 5, 2024
8e2dde0
Add TSDocs
afharo Dec 5, 2024
2ca3ce9
Fix i18n labels
afharo Dec 5, 2024
cac8be0
Fix types (mocks)
afharo Dec 5, 2024
acf77fd
Fix types (mocks)
afharo Dec 5, 2024
88ce365
Merge branch 'deprecations/log-sources-check-in-all-spaces' of github…
afharo Dec 10, 2024
6dae433
Merge branch 'main' of github.com:elastic/kibana into deprecations/lo…
afharo Dec 10, 2024
032f3b8
Fix docs + add unit tests
afharo Dec 10, 2024
b2bf260
Fix types
afharo Dec 10, 2024
cb0176b
Merge branch 'main' of github.com:elastic/kibana into deprecations/lo…
afharo Dec 10, 2024
dc649a8
Merge branch 'main' of github.com:elastic/kibana into deprecations/lo…
afharo Dec 11, 2024
cf9687e
Use `spaces.getAll()` API instead of new one from core
afharo Dec 11, 2024
1e5777d
[CI] Auto-commit changed files from 'node scripts/notice'
kibanamachine Dec 11, 2024
212ea7d
[CI] Auto-commit changed files from 'node scripts/yarn_deduplicate'
kibanamachine Dec 11, 2024
d759cbd
Fix types
afharo Dec 11, 2024
024bb68
Fix mock
afharo Dec 11, 2024
11716b9
Fix mock
afharo Dec 12, 2024
b0ad7c1
Fix another mock
afharo Dec 12, 2024
7776e31
Merge branch 'main' into deprecations/log-sources-check-in-all-spaces
afharo Dec 12, 2024
339a047
Merge branch 'main' into deprecations/log-sources-check-in-all-spaces
afharo Dec 16, 2024
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 @@ -168,7 +168,7 @@ export class SavedObjectsRepository implements ISavedObjectsRepository {
});
}

private constructor(options: SavedObjectsRepositoryOptions) {
private constructor(private readonly options: SavedObjectsRepositoryOptions) {
const {
index,
mappings,
Expand Down Expand Up @@ -564,4 +564,27 @@ export class SavedObjectsRepository implements ISavedObjectsRepository {
getCurrentNamespace(namespace?: string) {
return this.helpers.common.getCurrentNamespace(namespace);
}

/**
* {@inheritDoc ISavedObjectsRepository.getSearchableNamespaces}
*/
async getSearchableNamespaces(namespaces?: string[]) {
if (this.extensions.spacesExtension) {
return this.extensions.spacesExtension.getSearchableNamespaces(namespaces);
}
return [];
}

/**
* {@inheritDoc ISavedObjectsRepository.asScopedToNamespace}
*/
asScopedToNamespace(namespace: string) {
return new SavedObjectsRepository({
...this.options,
extensions: {
...this.options.extensions,
spacesExtension: this.extensions.spacesExtension?.asScopedToNamespace(namespace),
},
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const createRepositoryMock = () => {
collectMultiNamespaceReferences: jest.fn(),
updateObjectsSpaces: jest.fn(),
getCurrentNamespace: jest.fn(),
getSearchableNamespaces: jest.fn(),
asScopedToNamespace: jest.fn().mockImplementation(createRepositoryMock),
};

return mock;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const createSecurityExtension = (): jest.Mocked<ISavedObjectsSecurityExtension>
const createSpacesExtension = (): jest.Mocked<ISavedObjectsSpacesExtension> => ({
getCurrentNamespace: jest.fn(),
getSearchableNamespaces: jest.fn(),
asScopedToNamespace: jest.fn(),
});

const create = () => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,4 +216,14 @@ export class SavedObjectsClient implements SavedObjectsClientContract {
getCurrentNamespace() {
return this._repository.getCurrentNamespace();
}

/** {@inheritDoc SavedObjectsClientContract.getSearchableNamespaces} */
getSearchableNamespaces(namespaces?: string[]) {
return this._repository.getSearchableNamespaces(namespaces);
}

/** {@inheritDoc SavedObjectsClientContract.asScopedToNamespace} */
asScopedToNamespace(namespace: string) {
return new SavedObjectsClient(this._repository.asScopedToNamespace(namespace));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ const create = () => {
collectMultiNamespaceReferences: jest.fn(),
updateObjectsSpaces: jest.fn(),
getCurrentNamespace: jest.fn(),
getSearchableNamespaces: jest.fn(),
asScopedToNamespace: jest.fn(),
};

mock.createPointInTimeFinder = savedObjectsPointInTimeFinderMock.create({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const createSecurityExtension = (): jest.Mocked<ISavedObjectsSecurityExtension>
const createSpacesExtension = (): jest.Mocked<ISavedObjectsSpacesExtension> => ({
getCurrentNamespace: jest.fn(),
getSearchableNamespaces: jest.fn(),
asScopedToNamespace: jest.fn(),
});

const create = (): jest.Mocked<SavedObjectsExtensions> => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,4 +427,16 @@ export interface SavedObjectsClientContract {
* Returns the namespace associated with the client. If the namespace is the default one, this method returns `undefined`.
*/
getCurrentNamespace(): string | undefined;

/**
* Given a list of namespace strings, returns a subset that the user is authorized to search in.
* If a wildcard '*' is used, it is expanded to an explicit list of namespace strings.
Copy link
Contributor

Choose a reason for hiding this comment

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

We need to make it super clear that the explicit list of namespace strings only includes the namespaces the current user has access to othewise consumers might incorrectly interpret that as a list of all namespaces.

Suggested change
* If a wildcard '*' is used, it is expanded to an explicit list of namespace strings.
* If a wildcard '*' is used, it is expanded to an explicit list of namespace strings the current user has access to.

Copy link
Contributor

Choose a reason for hiding this comment

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

And it's worth noting that this will include any namespace the user has ANY access to.

*/
getSearchableNamespaces: (namespaces: string[] | undefined) => Promise<string[]>;

/**
* Returns a new Saved Objects client scoped to the new namespace.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* Returns a new Saved Objects client scoped to the new namespace.
* Returns a new Saved Objects client scoped to the current user and current namespace.

We're not creating a namespace, only a client that is scoped to one

* @param namespace Space to switch to.
afharo marked this conversation as resolved.
Show resolved Hide resolved
*/
asScopedToNamespace(namespace: string): SavedObjectsClientContract;
}
Original file line number Diff line number Diff line change
Expand Up @@ -552,4 +552,16 @@ export interface ISavedObjectsRepository {
* namespace.
*/
getCurrentNamespace(namespace?: string): string | undefined;

/**
* Given a list of namespace strings, returns a subset that the user is authorized to search in.
* If a wildcard '*' is used, it is expanded to an explicit list of namespace strings.
*/
getSearchableNamespaces: (namespaces: string[] | undefined) => Promise<string[]>;

/**
* Returns a new Saved Objects repository scoped to the new namespace.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* Returns a new Saved Objects repository scoped to the new namespace.
* Returns a new Saved Objects repository scoped to a specific namespace.

* @param namespace Space to switch to.
*/
asScopedToNamespace(namespace: string): ISavedObjectsRepository;
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,9 @@ export interface ISavedObjectsSpacesExtension {
* If a wildcard '*' is used, it is expanded to an explicit list of namespace strings.
*/
getSearchableNamespaces: (namespaces: string[] | undefined) => Promise<string[]>;
/**
* Returns a new Saved Objects Spaces Extension scoped to the new namespace.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* Returns a new Saved Objects Spaces Extension scoped to the new namespace.
* Returns a new Saved Objects Spaces Extension scoped to a specific namespace.

* @param namespace Space to switch to.
*/
asScopedToNamespace(namespace: string): ISavedObjectsSpacesExtension;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,67 +7,94 @@
import { DeprecationsDetails } from '@kbn/core-deprecations-common';
import { GetDeprecationsContext } from '@kbn/core-deprecations-server';
import { i18n } from '@kbn/i18n';
import pMap from 'p-map';
import { defaultLogViewId } from '../../common/log_views';
import { MIGRATE_LOG_VIEW_SETTINGS_URL } from '../../common/http_api/deprecations';
import { logSourcesKibanaAdvancedSettingRT } from '../../common';
import { LogsSharedPluginStartServicesAccessor } from '../types';

export const getLogSourcesSettingDeprecationInfo = async ({
getStartServices,
context,
}: {
export interface LogSourcesSettingDeprecationParams {
context: GetDeprecationsContext;
getStartServices: LogsSharedPluginStartServicesAccessor;
}): Promise<DeprecationsDetails[]> => {
const [_, pluginStartDeps, pluginStart] = await getStartServices();
const logSourcesService =
pluginStartDeps.logsDataAccess.services.logSourcesServiceFactory.getLogSourcesService(
context.savedObjectsClient
}

export const getLogSourcesSettingDeprecationInfo = async (
params: LogSourcesSettingDeprecationParams
): Promise<DeprecationsDetails[]> => {
const allAvailableSpaces = await params.context.savedObjectsClient.getSearchableNamespaces(['*']);
Copy link
Contributor

Choose a reason for hiding this comment

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

Requires end users to actively enable Log sources in each space.
We need that documented.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sorry, I don't fully understand this comment. We're reading a SO, and it's perfectly fine if the SO doesn't exist. Why would we need Log sources to be enabled in each space for this logic to work?


const deprecationPerSpaceFactory = getLogSourcesSettingDeprecationInfoForSpaceFactory(params);

const deprecations = await pMap(allAvailableSpaces, deprecationPerSpaceFactory, {
concurrency: 20, // limit the number of spaces handled concurrently to make sure that we cover large deployments
});

// unnest results
return ([] as DeprecationsDetails[]).concat(...deprecations);
};

export const getLogSourcesSettingDeprecationInfoForSpaceFactory = ({
getStartServices,
context,
}: LogSourcesSettingDeprecationParams): ((spaceId: string) => Promise<DeprecationsDetails[]>) => {
return async (spaceId) => {
const [_, pluginStartDeps, pluginStart] = await getStartServices();

// Get a new Saved Object Client scoped to the spaceId
const spaceScopedSavedObjectsClient = context.savedObjectsClient.asScopedToNamespace(spaceId);

const logSourcesService =
pluginStartDeps.logsDataAccess.services.logSourcesServiceFactory.getLogSourcesService(
spaceScopedSavedObjectsClient
);
const logViewsClient = pluginStart.logViews.getClient(
spaceScopedSavedObjectsClient,
context.esClient.asCurrentUser,
logSourcesService
);
const logViewsClient = pluginStart.logViews.getClient(
context.savedObjectsClient,
context.esClient.asCurrentUser,
logSourcesService
);

const logView = await logViewsClient.getLogView(defaultLogViewId);
const logView = await logViewsClient.getLogView(defaultLogViewId);

if (logView && !logSourcesKibanaAdvancedSettingRT.is(logView.attributes.logIndices)) {
return [
{
title: i18n.translate(
'xpack.logsShared.deprecations.migrateLogViewSettingsToLogSourcesSetting.title',
{
defaultMessage: 'Log sources setting',
}
),
level: 'warning',
deprecationType: 'feature',
message: i18n.translate(
'xpack.logsShared.deprecations.migrateLogViewSettingsToLogSourcesSetting.message',
{
defaultMessage:
'Indices and Data view options previously provided via the Logs UI settings page are now deprecated. Please migrate to using the Kibana log sources advanced setting.',
}
),
correctiveActions: {
manualSteps: [
i18n.translate(
'xpack.logsShared.deprecations.migrateLogViewSettingsToLogSourcesSetting.message.manualStepMessage',
{
defaultMessage:
'Update the Log sources Kibana advanced setting (via Management > Advanced Settings) to match the setting previously provided via the Logs UI settings page. Then via the Logs UI settings page use the Kibana log sources advanced setting option.',
}
),
],
api: {
method: 'PUT',
path: MIGRATE_LOG_VIEW_SETTINGS_URL,
if (logView && !logSourcesKibanaAdvancedSettingRT.is(logView.attributes.logIndices)) {
return [
{
title: i18n.translate(
'xpack.logsShared.deprecations.migrateLogViewSettingsToLogSourcesSetting.title',
{
defaultMessage: 'Log sources setting in the space "{spaceId}"',
values: { spaceId },
}
),
level: 'warning',
deprecationType: 'feature',
message: i18n.translate(
'xpack.logsShared.deprecations.migrateLogViewSettingsToLogSourcesSetting.message',
{
defaultMessage:
'Indices and Data view options previously provided via the Logs UI settings page are now deprecated. Please migrate to using the Kibana log sources advanced setting in the space "{spaceId}".',
values: { spaceId },
}
),
correctiveActions: {
manualSteps: [
i18n.translate(
'xpack.logsShared.deprecations.migrateLogViewSettingsToLogSourcesSetting.message.manualStepMessage',
{
defaultMessage:
'While in the space "{spaceId}" update the Log sources Kibana advanced setting (via Management > Advanced Settings) to match the setting previously provided via the Logs UI settings page. Then via the Logs UI settings page use the Kibana log sources advanced setting option.',
values: { spaceId },
}
),
],
api: {
method: 'PUT',
path: MIGRATE_LOG_VIEW_SETTINGS_URL,
},
},
},
},
];
} else {
return [];
}
];
} else {
return [];
}
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import pMap from 'p-map';
import { defaultLogViewId } from '../../../common/log_views';
import { MIGRATE_LOG_VIEW_SETTINGS_URL } from '../../../common/http_api/deprecations';
import { logSourcesKibanaAdvancedSettingRT } from '../../../common';
Expand All @@ -23,17 +24,51 @@ export const initMigrateLogViewSettingsRoute = ({
{ path: MIGRATE_LOG_VIEW_SETTINGS_URL, validate: false },
async (context, request, response) => {
try {
const { elasticsearch, savedObjects } = await context.core;
const allAvailableSpaces = await savedObjects.client.getSearchableNamespaces(['*']);

const [_, pluginStartDeps, pluginStart] = await getStartServices();

const logSourcesService =
await pluginStartDeps.logsDataAccess.services.logSourcesServiceFactory.getScopedLogSourcesService(
request
);
const logViewsClient = pluginStart.logViews.getScopedClient(request);
const updated = await pMap(
allAvailableSpaces,
async (spaceId) => {
const spaceScopedSavedObjectsClient = savedObjects.client.asScopedToNamespace(spaceId);

const logSourcesServicePromise =
pluginStartDeps.logsDataAccess.services.logSourcesServiceFactory.getLogSourcesService(
spaceScopedSavedObjectsClient
);
const logViewsClient = pluginStart.logViews.getClient(
spaceScopedSavedObjectsClient,
elasticsearch.client.asCurrentUser,
logSourcesServicePromise
);

const logView = await logViewsClient.getLogView(defaultLogViewId);

if (!logView || logSourcesKibanaAdvancedSettingRT.is(logView.attributes.logIndices)) {
return false;
}

const indices = (
await logViewsClient.getResolvedLogView({
type: 'log-view-reference',
logViewId: defaultLogViewId,
})
).indices;

const logSourcesService = await logSourcesServicePromise;
await logSourcesService.setLogSources([{ indexPattern: indices }]);
await logViewsClient.putLogView(defaultLogViewId, {
logIndices: { type: 'kibana_advanced_setting' },
});

const logView = await logViewsClient.getLogView(defaultLogViewId);
return true;
},
{ concurrency: 20 }
);

if (!logView || logSourcesKibanaAdvancedSettingRT.is(logView.attributes.logIndices)) {
if (!updated.includes(true)) {
return response.customError({
body: new Error(
"Unable to migrate log view settings. A log view either doesn't exist or is already using the Kibana advanced setting."
Expand All @@ -42,17 +77,6 @@ export const initMigrateLogViewSettingsRoute = ({
});
}

const indices = (
await logViewsClient.getResolvedLogView({
type: 'log-view-reference',
logViewId: defaultLogViewId,
})
).indices;

await logSourcesService.setLogSources([{ indexPattern: indices }]);
await logViewsClient.putLogView(defaultLogViewId, {
logIndices: { type: 'kibana_advanced_setting' },
});
return response.ok();
} catch (error) {
throw error;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,11 @@ export class SavedObjectsSpacesExtension implements ISavedObjectsSpacesExtension
);
}
}

asScopedToNamespace(namespace: string) {
return new SavedObjectsSpacesExtension({
activeSpaceId: namespace,
spacesClient: this.spacesClient,
});
}
}
3 changes: 0 additions & 3 deletions x-pack/plugins/translations/translations/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -27374,9 +27374,6 @@
"xpack.logsShared.dataSearch.cancelButtonLabel": "Annuler la demande",
"xpack.logsShared.dataSearch.loadingErrorRetryButtonLabel": "Réessayer",
"xpack.logsShared.dataSearch.shardFailureErrorMessage": "Index {indexName} : {errorMessage}",
"xpack.logsShared.deprecations.migrateLogViewSettingsToLogSourcesSetting.message": "Les options d'affichage des indices et des données précédemment fournies par le biais de la page des paramètres de l'interface utilisateur des logs sont désormais obsolètes. Veuillez désormais utiliser le paramètre avancé des sources de logs Kibana.",
"xpack.logsShared.deprecations.migrateLogViewSettingsToLogSourcesSetting.message.manualStepMessage": "Mettez à jour le paramètre avancé des sources de logs Kibana (via Gestion > Paramètres avancés) pour qu'il corresponde au paramètre précédemment fourni via la page des paramètres de l'interface utilisateur des logs. Ensuite, via la page des paramètres de l'interface utilisateur des logs, utilisez l'option de paramètre avancé des sources de logs Kibana.",
"xpack.logsShared.deprecations.migrateLogViewSettingsToLogSourcesSetting.title": "Paramétrage des sources de logs",
"xpack.logsShared.lobs.logEntryActionsViewInContextButton": "Afficher en contexte",
"xpack.logsShared.logEntryActionsMenu.apmActionLabel": "Afficher dans APM",
"xpack.logsShared.logEntryActionsMenu.buttonLabel": "Examiner",
Expand Down
3 changes: 0 additions & 3 deletions x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -27236,9 +27236,6 @@
"xpack.logsShared.dataSearch.cancelButtonLabel": "リクエストのキャンセル",
"xpack.logsShared.dataSearch.loadingErrorRetryButtonLabel": "再試行",
"xpack.logsShared.dataSearch.shardFailureErrorMessage": "インデックス {indexName}:{errorMessage}",
"xpack.logsShared.deprecations.migrateLogViewSettingsToLogSourcesSetting.message": "以前はログUI設定ページで提供されていたインデックスとデータ表示オプションは、現在では廃止予定です。Kibanaログソースの詳細設定を使用するように移行してください。",
"xpack.logsShared.deprecations.migrateLogViewSettingsToLogSourcesSetting.message.manualStepMessage": "ログソースKibana詳細設定([管理]>[詳細設定])を、以前にLogs UI設定ページで指定した設定と一致するように更新します。次に、ログUI設定ページで、Kibanaログソースの詳細設定オプションを使用します。",
"xpack.logsShared.deprecations.migrateLogViewSettingsToLogSourcesSetting.title": "ログソース設定",
"xpack.logsShared.lobs.logEntryActionsViewInContextButton": "コンテキストで表示",
"xpack.logsShared.logEntryActionsMenu.apmActionLabel": "APMで表示",
"xpack.logsShared.logEntryActionsMenu.buttonLabel": "調査",
Expand Down
Loading
Loading