[Logs Shared] Disable log view saved object registration for serverless (#165243)

## 📓 Summary

Closes #165222 

This work intends to avoid the registration of the log view saved object
(`type: "infrastructure-monitoring-log-view"`) and the related endpoint
to update a persisted log view.
To achieve it, the `xpack.logs_shared.savedObjects.logView.enabled`
configuration is introduced, which is configurable only in a serverless
environment and defaults to `true` for the stateful Kibana version to
keep the existing behaviour unchanged.

To also guarantee the normal functioning of the consumers of the
`<LogStream />` component in a serverless environment, we now set an
internal view using the default configuration that will be used as a
fallback when skipping the persisted log view lookup.

## 🧪 Testing

### Normal behaviour

When Kibana is used as always, we want to keep the current behaviour and
retrieve the persisted log view if exists or default to an internally
defined one.
- Launch the Kibana dev environment with `yarn start`
- Navigate to APM/Fleet/Enterprise Search logs
- Verify all the usages of the `<LogStream />` component work correctly
retrieving the internal views set during the setup lifecycle of the
related plugin. This means the LogStream should render the appropriate
columns set for the default log view or apply the custom ones defined
with an internal log view.

### Serverless behaviour

When Kibana is used in serverless mode, we want to skip the lookup for
the log view saved object and directly retrieve the log view from the
internally defined ones.
- Launch the Kibana dev environment with `yarn serverless-oblt`
- Navigate to APM/Fleet/Enterprise Search logs
- Verify all the usages of the `<LogStream />` component work correctly
retrieving the internal views set during the setup lifecycle of the
related plugin.

---------

Co-authored-by: Marco Antonio Ghiani <marcoantonio.ghiani@elastic.co>
This commit is contained in:
Marco Antonio Ghiani 2023-09-20 10:13:40 +02:00 committed by GitHub
parent ca1df9ec4f
commit f1ed91431a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 97 additions and 44 deletions

View file

@ -0,0 +1,14 @@
/*
* 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 interface LogsSharedConfig {
savedObjects: {
logView: {
enabled: boolean;
};
};
}

View file

@ -0,0 +1,29 @@
/*
* 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.
*/
import { schema, offeringBasedSchema } from '@kbn/config-schema';
import { PluginConfigDescriptor } from '@kbn/core/server';
import { LogsSharedConfig } from '../common/plugin_config';
export const configSchema = schema.object({
savedObjects: schema.object({
logView: schema.object({
enabled: offeringBasedSchema({
serverless: schema.boolean({
defaultValue: false,
}),
options: {
defaultValue: true,
},
}),
}),
}),
});
export const config: PluginConfigDescriptor<LogsSharedConfig> = {
schema: configSchema,
};

View file

@ -14,6 +14,7 @@ export type {
ILogsSharedLogEntriesDomain,
} from './lib/domains/log_entries_domain';
export { config } from './config';
export { logViewSavedObjectName } from './saved_objects';
export function plugin(context: PluginInitializerContext) {

View file

@ -7,6 +7,7 @@
import type { Logger } from '@kbn/logging';
import type { IBasePath } from '@kbn/core/server';
import { LogsSharedConfig } from '../../common/plugin_config';
import type { LogsSharedPluginStartServicesAccessor, UsageCollector } from '../types';
import type { KibanaFramework } from './adapters/framework/kibana_framework_adapter';
import type { ILogsSharedLogEntriesDomain } from './domains/log_entries_domain';
@ -17,8 +18,9 @@ export interface LogsSharedDomainLibs {
export interface LogsSharedBackendLibs extends LogsSharedDomainLibs {
basePath: IBasePath;
config: LogsSharedConfig;
framework: KibanaFramework;
getStartServices: LogsSharedPluginStartServicesAccessor;
logger: Logger;
getUsageCollector: () => UsageCollector;
logger: Logger;
}

View file

@ -23,6 +23,7 @@ import { LogsSharedBackendLibs, LogsSharedDomainLibs } from './lib/logs_shared_t
import { LogsSharedLogEntriesDomain } from './lib/domains/log_entries_domain';
import { LogsSharedKibanaLogEntriesAdapter } from './lib/adapters/log_entries/kibana_log_entries_adapter';
import { LogEntriesService } from './services/log_entries';
import { LogsSharedConfig } from '../common/plugin_config';
export class LogsSharedPlugin
implements
@ -34,11 +35,13 @@ export class LogsSharedPlugin
>
{
private readonly logger: Logger;
private config: LogsSharedConfig;
private libs!: LogsSharedBackendLibs;
private logViews: LogViewsService;
private usageCollector: UsageCollector;
constructor(context: PluginInitializerContext) {
constructor(context: PluginInitializerContext<LogsSharedConfig>) {
this.config = context.config.get();
this.logger = context.logger.get();
this.usageCollector = {};
@ -50,8 +53,13 @@ export class LogsSharedPlugin
const logViews = this.logViews.setup();
// Register saved objects
core.savedObjects.registerType(logViewSavedObjectType);
if (this.config.savedObjects.logView.enabled) {
// Conditionally register log view saved objects
core.savedObjects.registerType(logViewSavedObjectType);
} else {
// Register a static internal view to use as a fallback when the log view SO is not registered
logViews.defineInternalLogView('default', {});
}
const domainLibs: LogsSharedDomainLibs = {
logEntries: new LogsSharedLogEntriesDomain(new LogsSharedKibanaLogEntriesAdapter(framework), {
@ -63,10 +71,11 @@ export class LogsSharedPlugin
this.libs = {
...domainLibs,
basePath: core.http.basePath,
config: this.config,
framework,
getStartServices: () => core.getStartServices(),
logger: this.logger,
getUsageCollector: () => this.usageCollector,
logger: this.logger,
};
// Register server side APIs

View file

@ -8,16 +8,13 @@
import { logViewsV1 } from '../../../common/http_api';
import { LOG_VIEW_URL } from '../../../common/http_api/log_views';
import { createValidationFunction } from '../../../common/runtime_types';
import type { KibanaFramework } from '../../lib/adapters/framework/kibana_framework_adapter';
import type { LogsSharedPluginStartServicesAccessor } from '../../types';
import { LogsSharedBackendLibs } from '../../lib/logs_shared_types';
export const initGetLogViewRoute = ({
config,
framework,
getStartServices,
}: {
framework: KibanaFramework;
getStartServices: LogsSharedPluginStartServicesAccessor;
}) => {
}: LogsSharedBackendLibs) => {
framework
.registerVersionedRoute({
access: 'internal',
@ -39,7 +36,14 @@ export const initGetLogViewRoute = ({
const logViewsClient = logViews.getScopedClient(request);
try {
const logView = await logViewsClient.getLogView(logViewId);
/**
* Two possible paths for retrieving the log view
* - if the log view saved object is correctly registered, perform a lookup for retrieving it
* - else, skip the saved object lookup and immediately get the internal log view if exists.
*/
const logView = config.savedObjects.logView.enabled
? await logViewsClient.getLogView(logViewId)
: await logViewsClient.getInternalLogView(logViewId);
return response.ok({
body: logViewsV1.getLogViewResponsePayloadRT.encode({

View file

@ -4,16 +4,15 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { KibanaFramework } from '../../lib/adapters/framework/kibana_framework_adapter';
import { LogsSharedPluginStartServicesAccessor } from '../../types';
import { LogsSharedBackendLibs } from '../../lib/logs_shared_types';
import { initGetLogViewRoute } from './get_log_view';
import { initPutLogViewRoute } from './put_log_view';
export const initLogViewRoutes = (dependencies: {
framework: KibanaFramework;
getStartServices: LogsSharedPluginStartServicesAccessor;
}) => {
initGetLogViewRoute(dependencies);
initPutLogViewRoute(dependencies);
export const initLogViewRoutes = (libs: LogsSharedBackendLibs) => {
initGetLogViewRoute(libs);
// Register the log view update endpoint only when the Saved object is correctly registered
if (libs.config.savedObjects.logView.enabled) {
initPutLogViewRoute(libs);
}
};

View file

@ -8,16 +8,9 @@
import { logViewsV1 } from '../../../common/http_api';
import { LOG_VIEW_URL } from '../../../common/http_api/log_views';
import { createValidationFunction } from '../../../common/runtime_types';
import type { KibanaFramework } from '../../lib/adapters/framework/kibana_framework_adapter';
import type { LogsSharedPluginStartServicesAccessor } from '../../types';
import { LogsSharedBackendLibs } from '../../lib/logs_shared_types';
export const initPutLogViewRoute = ({
framework,
getStartServices,
}: {
framework: KibanaFramework;
getStartServices: LogsSharedPluginStartServicesAccessor;
}) => {
export const initPutLogViewRoute = ({ framework, getStartServices }: LogsSharedBackendLibs) => {
framework
.registerVersionedRoute({
access: 'internal',

View file

@ -11,6 +11,7 @@ import type { ILogViewsClient } from './types';
export const createLogViewsClientMock = (): jest.Mocked<ILogViewsClient> => ({
getLogView: jest.fn(),
getInternalLogView: jest.fn(),
getResolvedLogView: jest.fn((logViewReference: LogViewReference) =>
Promise.resolve(createResolvedLogViewMock())
),

View file

@ -64,6 +64,20 @@ export class LogViewsClient implements ILogViewsClient {
);
}
public async getInternalLogView(logViewId: string): Promise<LogView> {
this.logger.debug(`Trying to load internal log view "${logViewId}"...`);
const internalLogView = this.internalLogViews.get(logViewId);
if (!internalLogView) {
throw new NotFoundError(
`Failed to load internal log view: no view with id "${logViewId}" found.`
);
}
return internalLogView;
}
public async getResolvedLogView(logViewReference: LogViewReference): Promise<ResolvedLogView> {
const logView = persistedLogViewReferenceRT.is(logViewReference)
? await this.getLogView(logViewReference.logViewId)
@ -125,20 +139,6 @@ export class LogViewsClient implements ILogViewsClient {
return getLogViewFromSavedObject(savedObject);
}
private async getInternalLogView(logViewId: string): Promise<LogView> {
this.logger.debug(`Trying to load internal log view "${logViewId}"...`);
const internalLogView = this.internalLogViews.get(logViewId);
if (!internalLogView) {
throw new NotFoundError(
`Failed to load internal log view: no view with id "${logViewId}" found.`
);
}
return internalLogView;
}
private async getLogViewFromLogsSharedSourceConfiguration(sourceId: string): Promise<LogView> {
this.logger.debug(`Trying to load log view from fallback configuration "${sourceId}"...`);

View file

@ -52,6 +52,7 @@ export interface LogViewsServiceStart {
export interface ILogViewsClient {
getLogView(logViewId: string): Promise<LogView>;
getInternalLogView(logViewId: string): Promise<LogView>;
getResolvedLogView(logView: LogViewReference): Promise<ResolvedLogView>;
putLogView(logViewId: string, logViewAttributes: Partial<LogViewAttributes>): Promise<LogView>;
resolveLogView(logViewId: string, logViewAttributes: LogViewAttributes): Promise<ResolvedLogView>;