Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion src/plugins/usage_collection/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { PluginInitializerContext } from '../../../core/public';
import { UsageCollectionPlugin } from './plugin';

export { METRIC_TYPE } from '@kbn/analytics';
export { UsageCollectionSetup } from './plugin';
Copy link
Member Author

Choose a reason for hiding this comment

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

This is here because I missed exporting it when I updated the UsageCollection plugin start contract here: #69836

export { UsageCollectionSetup, UsageCollectionStart } from './plugin';

export function plugin(initializerContext: PluginInitializerContext) {
return new UsageCollectionPlugin(initializerContext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@

import { i18n } from '@kbn/i18n';
import { sum } from 'lodash';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { FetchDataParams } from '../../../../observability/public/data_handler';
import { ApmFetchDataResponse } from '../../../../observability/public/typings/fetch_data_response';
import {
ApmFetchDataResponse,
FetchDataParams,
} from '../../../../observability/public';
import { callApmApi } from './createCallApmApi';
import { Theme } from '../../utils/get_theme';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { rangeFilter } from '../../../common/utils/range_filter';
import { Coordinates } from '../../../../observability/public/typings/fetch_data_response';
import { Coordinates } from '../../../../observability/public';
import { PROCESSOR_EVENT } from '../../../common/elasticsearch_fieldnames';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { ProcessorEvent } from '../../../common/processor_event';
Expand Down
4 changes: 1 addition & 3 deletions x-pack/plugins/infra/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@
"alerts",
"triggers_actions_ui"
],
"optionalPlugins": [
"ml"
],
"optionalPlugins": ["ml", "observability"],
"server": true,
"ui": true,
"configPath": ["xpack", "infra"]
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/infra/public/apps/common_providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
KibanaContextProvider,
} from '../../../../../src/plugins/kibana_react/public';
import { TriggersActionsProvider } from '../utils/triggers_actions_context';
import { ClientPluginDeps } from '../types';
Copy link
Member Author

Choose a reason for hiding this comment

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

I changed some names here to try to make it all consistent. We now have most of our types prefaced with "InfraClient" (eventually to be LogsClient and MetricsClient, with "Client" to distinguish from the "Server" types), and then we use StartDeps/StartExports and SetupDeps/SetupExports to specify the dependencies and exports coming and going out of each lifecycle method.

import { InfraClientStartDeps } from '../types';
import { TriggersAndActionsUIPublicPluginStart } from '../../../triggers_actions_ui/public';
import { ApolloClientContext } from '../utils/apollo_context';
import { EuiThemeProvider } from '../../../observability/public';
Expand All @@ -37,7 +37,7 @@ export const CommonInfraProviders: React.FC<{

export const CoreProviders: React.FC<{
core: CoreStart;
plugins: ClientPluginDeps;
plugins: InfraClientStartDeps;
}> = ({ children, core, plugins }) => {
return (
<KibanaContextProvider services={{ ...core, ...plugins }}>
Expand Down
6 changes: 3 additions & 3 deletions x-pack/plugins/infra/public/apps/logs_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ import '../index.scss';
import { NotFoundPage } from '../pages/404';
import { LinkToLogsPage } from '../pages/link_to/link_to_logs';
import { LogsPage } from '../pages/logs';
import { ClientPluginDeps } from '../types';
import { InfraClientStartDeps } from '../types';
import { createApolloClient } from '../utils/apollo_client';
import { CommonInfraProviders, CoreProviders } from './common_providers';
import { prepareMountElement } from './common_styles';

export const renderApp = (
core: CoreStart,
plugins: ClientPluginDeps,
plugins: InfraClientStartDeps,
{ element, history }: AppMountParameters
) => {
const apolloClient = createApolloClient(core.http.fetch);
Expand All @@ -43,7 +43,7 @@ const LogsApp: React.FC<{
apolloClient: ApolloClient<{}>;
core: CoreStart;
history: History<unknown>;
plugins: ClientPluginDeps;
plugins: InfraClientStartDeps;
}> = ({ apolloClient, core, history, plugins }) => {
const uiCapabilities = core.application.capabilities;

Expand Down
6 changes: 3 additions & 3 deletions x-pack/plugins/infra/public/apps/metrics_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ import { NotFoundPage } from '../pages/404';
import { LinkToMetricsPage } from '../pages/link_to/link_to_metrics';
import { InfrastructurePage } from '../pages/metrics';
import { MetricDetail } from '../pages/metrics/metric_detail';
import { ClientPluginDeps } from '../types';
import { InfraClientStartDeps } from '../types';
import { createApolloClient } from '../utils/apollo_client';
import { RedirectWithQueryParams } from '../utils/redirect_with_query_params';
import { CommonInfraProviders, CoreProviders } from './common_providers';
import { prepareMountElement } from './common_styles';

export const renderApp = (
core: CoreStart,
plugins: ClientPluginDeps,
plugins: InfraClientStartDeps,
{ element, history }: AppMountParameters
) => {
const apolloClient = createApolloClient(core.http.fetch);
Expand All @@ -45,7 +45,7 @@ const MetricsApp: React.FC<{
apolloClient: ApolloClient<{}>;
core: CoreStart;
history: History<unknown>;
plugins: ClientPluginDeps;
plugins: InfraClientStartDeps;
}> = ({ apolloClient, core, history, plugins }) => {
const uiCapabilities = core.application.capabilities;

Expand Down
17 changes: 11 additions & 6 deletions x-pack/plugins/infra/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@
*/

import { PluginInitializer, PluginInitializerContext } from 'kibana/public';
import { ClientSetup, ClientStart, Plugin } from './plugin';
import { ClientPluginsSetup, ClientPluginsStart } from './types';
import { Plugin } from './plugin';
import {
InfraClientSetupExports,
InfraClientStartExports,
InfraClientSetupDeps,
InfraClientStartDeps,
} from './types';
Copy link
Member Author

Choose a reason for hiding this comment

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


export const plugin: PluginInitializer<
ClientSetup,
ClientStart,
ClientPluginsSetup,
ClientPluginsStart
InfraClientSetupExports,
InfraClientStartExports,
InfraClientSetupDeps,
InfraClientStartDeps
> = (context: PluginInitializerContext) => {
return new Plugin(context);
};
Expand Down
63 changes: 26 additions & 37 deletions x-pack/plugins/infra/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,40 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import {
AppMountParameters,
CoreSetup,
CoreStart,
Plugin as PluginClass,
PluginInitializerContext,
} from 'kibana/public';
import { AppMountParameters, PluginInitializerContext } from 'kibana/public';
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public';
import { createMetricThresholdAlertType } from './alerting/metric_threshold';
import { createInventoryMetricAlertType } from './alerting/inventory';
import { getAlertType as getLogsAlertType } from './components/alerting/logs/log_threshold_alert_type';
import { registerStartSingleton } from './legacy_singletons';
import { registerFeatures } from './register_feature';
import { ClientPluginsSetup, ClientPluginsStart } from './types';

export type ClientSetup = void;
export type ClientStart = void;
import {
InfraClientSetupDeps,
InfraClientStartDeps,
InfraClientCoreSetup,
InfraClientCoreStart,
InfraClientPluginClass,
} from './types';
import { getLogsHasDataFetcher, getLogsOverviewDataFetcher } from './utils/logs_overview_fetchers';

export class Plugin
implements PluginClass<ClientSetup, ClientStart, ClientPluginsSetup, ClientPluginsStart> {
export class Plugin implements InfraClientPluginClass {
constructor(_context: PluginInitializerContext) {}

setup(core: CoreSetup<ClientPluginsStart, ClientStart>, pluginsSetup: ClientPluginsSetup) {
setup(core: InfraClientCoreSetup, pluginsSetup: InfraClientSetupDeps) {
registerFeatures(pluginsSetup.home);

pluginsSetup.triggers_actions_ui.alertTypeRegistry.register(createInventoryMetricAlertType());
pluginsSetup.triggers_actions_ui.alertTypeRegistry.register(getLogsAlertType());
pluginsSetup.triggers_actions_ui.alertTypeRegistry.register(createMetricThresholdAlertType());

if (pluginsSetup.observability) {
pluginsSetup.observability.dashboard.register({
appName: 'infra_logs',
hasData: getLogsHasDataFetcher(core.getStartServices),
fetchData: getLogsOverviewDataFetcher(core.getStartServices),
});
}

core.application.register({
id: 'logs',
title: i18n.translate('xpack.infra.logs.pluginTitle', {
Expand All @@ -43,20 +48,11 @@ export class Plugin
appRoute: '/app/logs',
category: DEFAULT_APP_CATEGORIES.observability,
mount: async (params: AppMountParameters) => {
// mount callback should not use setup dependencies, get start dependencies instead
const [coreStart, pluginsStart] = await core.getStartServices();
const { renderApp } = await import('./apps/logs_app');

return renderApp(
coreStart,
{
data: pluginsStart.data,
dataEnhanced: pluginsSetup.dataEnhanced,
home: pluginsSetup.home,
triggers_actions_ui: pluginsStart.triggers_actions_ui,
usageCollection: pluginsSetup.usageCollection,
},
params
);
return renderApp(coreStart, pluginsStart, params);
},
});

Expand All @@ -70,20 +66,11 @@ export class Plugin
appRoute: '/app/metrics',
category: DEFAULT_APP_CATEGORIES.observability,
mount: async (params: AppMountParameters) => {
// mount callback should not use setup dependencies, get start dependencies instead
const [coreStart, pluginsStart] = await core.getStartServices();
const { renderApp } = await import('./apps/metrics_app');

return renderApp(
coreStart,
{
data: pluginsStart.data,
dataEnhanced: pluginsSetup.dataEnhanced,
home: pluginsSetup.home,
triggers_actions_ui: pluginsStart.triggers_actions_ui,
usageCollection: pluginsSetup.usageCollection,
},
params
);
return renderApp(coreStart, pluginsStart, params);
},
});

Expand All @@ -102,7 +89,9 @@ export class Plugin
});
}

start(core: CoreStart, _plugins: ClientPluginsStart) {
start(core: InfraClientCoreStart, _plugins: InfraClientStartDeps) {
registerStartSingleton(core);
}

stop() {}
}
30 changes: 25 additions & 5 deletions x-pack/plugins/infra/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,42 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { CoreSetup, CoreStart, Plugin as PluginClass } from 'kibana/public';
import { DataPublicPluginStart } from '../../../../src/plugins/data/public';
import { HomePublicPluginSetup } from '../../../../src/plugins/home/public';
import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public';
import {
UsageCollectionSetup,
UsageCollectionStart,
} from '../../../../src/plugins/usage_collection/public';
import { TriggersAndActionsUIPublicPluginSetup } from '../../../plugins/triggers_actions_ui/public';
import { DataEnhancedSetup } from '../../data_enhanced/public';
import { DataEnhancedSetup, DataEnhancedStart } from '../../data_enhanced/public';
import { ObservabilityPluginSetup, ObservabilityPluginStart } from '../../observability/public';

export interface ClientPluginsSetup {
// Our own setup and start contract values
export type InfraClientSetupExports = void;
export type InfraClientStartExports = void;

export interface InfraClientSetupDeps {
dataEnhanced: DataEnhancedSetup;
home: HomePublicPluginSetup;
observability: ObservabilityPluginSetup;
triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup;
usageCollection: UsageCollectionSetup;
}

export interface ClientPluginsStart {
export interface InfraClientStartDeps {
data: DataPublicPluginStart;
dataEnhanced: DataEnhancedStart;
observability: ObservabilityPluginStart;
triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup;
usageCollection: UsageCollectionStart;
}

export type ClientPluginDeps = ClientPluginsSetup & ClientPluginsStart;
export type InfraClientCoreSetup = CoreSetup<InfraClientStartDeps, InfraClientStartExports>;
export type InfraClientCoreStart = CoreStart;
export type InfraClientPluginClass = PluginClass<
InfraClientSetupExports,
InfraClientStartExports,
InfraClientSetupDeps,
InfraClientStartDeps
>;
93 changes: 93 additions & 0 deletions x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { InfraClientCoreSetup } from '../types';
import { LogsFetchDataResponse } from '../../../observability/public';

export function getLogsHasDataFetcher(getStartServices: InfraClientCoreSetup['getStartServices']) {
Copy link
Contributor

Choose a reason for hiding this comment

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

@cauemarcondes I understand this function is a requirement for the dashboard. Is it a hard requirement?

I can imagine that the reason to have it is to not perform a potentially expensive query to fetch the data if there's no data to fetch.

if that's the case, how expensive would it be to call the data fetching function when there's no data at all, versus calling two functions every time?

Copy link
Contributor

Choose a reason for hiding this comment

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

@afgomez, the idea behind the hasData function is indeed to get a flag indicating if there's any data available in the logs indices. This exists because we have two pages in the Observability UI /landing and /overview plus /. The / URL, will call the hasData from all plugins, if one of the results is true we redirect the user to /overview otherwise /landing.

We could, of course, call the fetchData instead of hasData, but in that case, the response time would probably be twice or even thrice time slower then only checking if there's data available.

Also hasData must return false, if a user doesn't have privileges to read from the logs indices.

return async () => {
// if you need the data plugin, this is how you get it
// const [, startPlugins] = await getStartServices();
// const { data } = startPlugins;

// if you need a core dep, we need to pass in more than just getStartServices

// perform query
return true;
};
}

export function getLogsOverviewDataFetcher(
getStartServices: InfraClientCoreSetup['getStartServices']
) {
return async (): Promise<LogsFetchDataResponse> => {
// if you need the data plugin, this is how you get it
// const [, startPlugins] = await getStartServices();
// const { data } = startPlugins;

// if you need a core dep, we need to pass in more than just getStartServices

// perform query
return {
title: 'Log rate',
appLink: 'TBD', // TODO: what format should this be in, relative I assume?
Copy link
Contributor

Choose a reason for hiding this comment

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

i think for the sake of simplicity observability plugin should handle the relative part while generating link.

stats: {
nginx: {
type: 'number',
label: 'nginx',
value: 345341,
},
'elasticsearch.audit': {
type: 'number',
label: 'elasticsearch.audit',
value: 164929,
},
'haproxy.log': {
type: 'number',
label: 'haproxy.log',
value: 51101,
},
},
// Note: My understanding is that these series coordinates will be
// combined into objects that look like:
// { x: timestamp, y: value, g: label (e.g. nginx) }
// so they fit the stacked bar chart API
// https://elastic.github.io/elastic-charts/?path=/story/bar-chart--stacked-with-axis-and-legend
series: {
nginx: {
label: 'nginx',
coordinates: [
{ x: 1593000000000, y: 10014 },
{ x: 1593000900000, y: 12827 },
{ x: 1593001800000, y: 2946 },
{ x: 1593002700000, y: 14298 },
{ x: 1593003600000, y: 4096 },
],
},
'elasticsearch.audit': {
label: 'elasticsearch.audit',
coordinates: [
{ x: 1593000000000, y: 5676 },
{ x: 1593000900000, y: 6783 },
{ x: 1593001800000, y: 2394 },
{ x: 1593002700000, y: 4554 },
{ x: 1593003600000, y: 5659 },
],
},
'haproxy.log': {
label: 'haproxy.log',
coordinates: [
{ x: 1593000000000, y: 9085 },
{ x: 1593000900000, y: 9002 },
{ x: 1593001800000, y: 3940 },
{ x: 1593002700000, y: 5451 },
{ x: 1593003600000, y: 9133 },
],
},
},
};
};
}
Loading