[ObsUX] Add UI Setting for controling Profiling visibility in Infra (#173294)

Closes https://github.com/elastic/kibana/issues/173154

Adds a UI setting to control Infra+Profiling integration from Kibana's
Advanced Settings as well as from the Infra Settings screen.

Note that the plugin config feature flag is still there because I
realized we need it to disable Profiling integration in serverless.



2a5ace9d-9e18-49a4-be95-c722f24072a7

### How to test

* Make sure profiling is enabled in `kibana.dev.yml`
```
xpack.profiling.enabled: true
```
* Start kibana in traditional mode, go to Infra Settings
* Make sure there is the new toggle for Profiling integration and it's
on
* Go to one of your host's details and make sure you see the profiling
tab
* Toggle the Profiling integration setting off and check that the tap in
host details is not visible

* Start kibana in serverless mode
* Make sure there is no new setting neither in Infra Settings nor in
Advanced Settings
* Make sure Profiling tab is not visible in host details

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Mykola Harmash 2023-12-19 16:48:35 +01:00 committed by GitHub
parent 4fc4dfbbda
commit c627907733
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 152 additions and 20 deletions

View file

@ -451,6 +451,9 @@ preview:[] When enabled, allows users to create Service Groups from the APM Serv
[[observability-apm-trace-explorer-tab]]`observability:apmTraceExplorerTab`::
preview:[] Enable the APM Trace Explorer feature, that allows you to search and inspect traces with KQL or EQL.
[[observability-infrastructure-profiling-integration]]`observability:enableInfrastructureProfilingIntegration`::
preview:[] Enables the Profiling view in Host details within Infrastructure.
[float]
[[kibana-reporting-settings]]
==== Reporting

View file

@ -581,6 +581,10 @@ export const stackManagementSchema: MakeSchemaFrom<UsageStats> = {
type: 'boolean',
_meta: { description: 'Non-default value of setting.' },
},
'observability:enableInfrastructureProfilingIntegration': {
type: 'boolean',
_meta: { description: 'Non-default value of setting.' },
},
'securitySolution:enableGroupedNav': {
type: 'boolean',
_meta: { description: 'Non-default value of setting.' },

View file

@ -46,6 +46,7 @@ export interface UsageStats {
'observability:apmAWSLambdaPriceFactor': string;
'observability:apmAWSLambdaRequestCostPerMillion': number;
'observability:enableInfrastructureHostsView': boolean;
'observability:enableInfrastructureProfilingIntegration': boolean;
'observability:apmAgentExplorerView': boolean;
'visualization:heatmap:maxBuckets': number;
'visualization:colorMapping': string;

View file

@ -10061,6 +10061,12 @@
"description": "Non-default value of setting."
}
},
"observability:enableInfrastructureProfilingIntegration": {
"type": "boolean",
"_meta": {
"description": "Non-default value of setting."
}
},
"securitySolution:enableGroupedNav": {
"type": "boolean",
"_meta": {

View file

@ -5,22 +5,23 @@
* 2.0.
*/
import {
EuiIcon,
type EuiPageHeaderProps,
type EuiBreadcrumbsProps,
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
type EuiBreadcrumbsProps,
type EuiPageHeaderProps,
} from '@elastic/eui';
import { useLinkProps } from '@kbn/observability-shared-plugin/public';
import React, { useCallback, useMemo } from 'react';
import { capitalize } from 'lodash';
import { useHistory, useLocation } from 'react-router-dom';
import { FormattedMessage } from '@kbn/i18n-react';
import { useLinkProps } from '@kbn/observability-shared-plugin/public';
import { capitalize } from 'lodash';
import React, { useCallback, useMemo } from 'react';
import { useHistory, useLocation } from 'react-router-dom';
import { usePluginConfig } from '../../../containers/plugin_config_context';
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';
import { useProfilingIntegrationSetting } from '../../../hooks/use_profiling_integration_setting';
import { APM_HOST_FILTER_FIELD } from '../constants';
import { LinkToAlertsRule, LinkToApmServices, LinkToNodeDetails } from '../links';
import { ContentTabIds, type RouteState, type LinkOptions, type Tab, type TabIds } from '../types';
import { ContentTabIds, type LinkOptions, type RouteState, type Tab, type TabIds } from '../types';
import { useAssetDetailsRenderPropsContext } from './use_asset_details_render_props';
import { useTabSwitcherContext } from './use_tab_switcher';
@ -110,12 +111,14 @@ const useRightSideItems = (links?: LinkOptions[]) => {
const useFeatureFlagTabs = () => {
const { featureFlags } = usePluginConfig();
const isProfilingEnabled = useProfilingIntegrationSetting();
const featureFlagControlledTabs: Partial<Record<ContentTabIds, boolean>> = useMemo(
() => ({
[ContentTabIds.OSQUERY]: featureFlags.osqueryEnabled,
[ContentTabIds.PROFILING]: featureFlags.profilingEnabled,
[ContentTabIds.PROFILING]: isProfilingEnabled,
}),
[featureFlags.osqueryEnabled, featureFlags.profilingEnabled]
[featureFlags.osqueryEnabled, isProfilingEnabled]
);
const isTabEnabled = useCallback(

View file

@ -10,19 +10,24 @@ import { i18n } from '@kbn/i18n';
import { EuiButtonEmpty } from '@elastic/eui';
import { EuiBadge } from '@elastic/eui';
import { EuiFlexGroup } from '@elastic/eui';
import { usePluginConfig } from '../../../../../containers/plugin_config_context';
import { useProfilingIntegrationSetting } from '../../../../../hooks/use_profiling_integration_setting';
import { useTabSwitcherContext } from '../../../hooks/use_tab_switcher';
export function CpuProfilingPrompt() {
const { showTab } = useTabSwitcherContext();
const { featureFlags } = usePluginConfig();
const isProfilingEnabled = useProfilingIntegrationSetting();
if (!featureFlags.profilingEnabled) {
if (!isProfilingEnabled) {
return null;
}
return (
<EuiFlexGroup alignItems="center" justifyContent="flexStart" gutterSize="s">
<EuiFlexGroup
alignItems="center"
justifyContent="flexStart"
gutterSize="s"
data-test-subj="infraAssetDetailsCPUProfilingPrompt"
>
<EuiBadge color="success">
{i18n.translate('xpack.infra.cpuProfilingPrompt.newBadgeLabel', {
defaultMessage: 'NEW',

View file

@ -0,0 +1,21 @@
/*
* 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 { useUiSetting } from '@kbn/kibana-react-plugin/public';
import { enableInfrastructureProfilingIntegration } from '@kbn/observability-plugin/common';
import { usePluginConfig } from '../containers/plugin_config_context';
export function useProfilingIntegrationSetting(): boolean {
const {
featureFlags: { profilingEnabled },
} = usePluginConfig();
const isProfilingUiSettingEnabled = useUiSetting<boolean>(
enableInfrastructureProfilingIntegration
);
return profilingEnabled && isProfilingUiSettingEnabled;
}

View file

@ -10,9 +10,13 @@ import { EuiSpacer } from '@elastic/eui';
import { EuiForm } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import React from 'react';
import { enableInfrastructureHostsView } from '@kbn/observability-plugin/common';
import {
enableInfrastructureHostsView,
enableInfrastructureProfilingIntegration,
} from '@kbn/observability-plugin/common';
import { useEditableSettings } from '@kbn/observability-shared-plugin/public';
import { LazyField } from '@kbn/advanced-settings-plugin/public';
import { usePluginConfig } from '../../../containers/plugin_config_context';
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';
type Props = Pick<
@ -31,6 +35,7 @@ export function FeaturesConfigurationPanel({
const {
services: { docLinks, notifications },
} = useKibanaContextForPlugin();
const { featureFlags } = usePluginConfig();
return (
<EuiForm>
@ -52,6 +57,17 @@ export function FeaturesConfigurationPanel({
toasts={notifications.toasts}
unsavedChanges={unsavedChanges[enableInfrastructureHostsView]}
/>
{featureFlags.profilingEnabled && (
<LazyField
key={enableInfrastructureProfilingIntegration}
setting={settingsEditableConfig[enableInfrastructureProfilingIntegration]}
handleChange={handleFieldChange}
enableSaving={!readOnly}
docLinks={docLinks.links}
toasts={notifications.toasts}
unsavedChanges={unsavedChanges[enableInfrastructureProfilingIntegration]}
/>
)}
</EuiForm>
);
}

View file

@ -17,7 +17,10 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import React, { useCallback } from 'react';
import { Prompt, useEditableSettings } from '@kbn/observability-shared-plugin/public';
import { enableInfrastructureHostsView } from '@kbn/observability-plugin/common';
import {
enableInfrastructureHostsView,
enableInfrastructureProfilingIntegration,
} from '@kbn/observability-plugin/common';
import { SourceLoadingPage } from '../../../components/source_loading_page';
import { useSourceContext } from '../../../containers/metrics_source';
import { useInfraMLCapabilitiesContext } from '../../../containers/ml/infra_ml_capabilities';
@ -61,7 +64,10 @@ export const SourceConfigurationSettings = ({
formState,
formStateChanges,
} = useSourceConfigurationFormState(source && source.configuration);
const infraUiSettings = useEditableSettings('infra_metrics', [enableInfrastructureHostsView]);
const infraUiSettings = useEditableSettings('infra_metrics', [
enableInfrastructureHostsView,
enableInfrastructureProfilingIntegration,
]);
const resetAllUnsavedChanges = useCallback(() => {
resetForm();

View file

@ -32,6 +32,7 @@ export {
apmTraceExplorerTab,
apmLabsButton,
enableInfrastructureHostsView,
enableInfrastructureProfilingIntegration,
enableAwsLambdaMetrics,
enableAgentExplorerView,
apmAWSLambdaPriceFactor,

View file

@ -17,6 +17,8 @@ export const apmServiceGroupMaxNumberOfServices =
export const apmTraceExplorerTab = 'observability:apmTraceExplorerTab';
export const apmLabsButton = 'observability:apmLabsButton';
export const enableInfrastructureHostsView = 'observability:enableInfrastructureHostsView';
export const enableInfrastructureProfilingIntegration =
'observability:enableInfrastructureProfilingIntegration';
export const enableAwsLambdaMetrics = 'observability:enableAwsLambdaMetrics';
export const enableAgentExplorerView = 'observability:apmAgentExplorerView';
export const apmAWSLambdaPriceFactor = 'observability:apmAWSLambdaPriceFactor';

View file

@ -37,6 +37,7 @@ import {
profilingPervCPUWattArm64,
profilingAWSCostDiscountRate,
profilingCostPervCPUPerHour,
enableInfrastructureProfilingIntegration,
} from '../common/ui_settings_keys';
const betaLabel = i18n.translate('xpack.observability.uiSettings.betaLabel', {
@ -236,6 +237,24 @@ export const uiSettings: Record<string, UiSettings> = {
}),
schema: schema.boolean(),
},
[enableInfrastructureProfilingIntegration]: {
category: [observabilityFeatureId],
name: i18n.translate('xpack.observability.enableInfrastructureProfilingIntegration', {
defaultMessage: 'Universal Profiling integration in Infrastructure',
}),
value: true,
description: i18n.translate(
'xpack.observability.enableInfrastructureProfilingIntegrationDescription',
{
defaultMessage:
'{betaLabel} Enable Universal Profiling integration in the Infrastructure app.',
values: {
betaLabel: `<em>[${betaLabel}]</em>`,
},
}
),
schema: schema.boolean(),
},
[enableAwsLambdaMetrics]: {
category: [observabilityFeatureId],
name: i18n.translate('xpack.observability.enableAwsLambdaMetrics', {
@ -415,9 +434,9 @@ export const uiSettings: Record<string, UiSettings> = {
}),
value: 1.7,
description: i18n.translate('xpack.observability.profilingDatacenterPUEUiSettingDescription', {
defaultMessage: `Data center power usage effectiveness (PUE) measures how efficiently a data center uses energy. Defaults to 1.7, the average on-premise data center PUE according to the {uptimeLink} survey
defaultMessage: `Data center power usage effectiveness (PUE) measures how efficiently a data center uses energy. Defaults to 1.7, the average on-premise data center PUE according to the {uptimeLink} survey
</br></br>
You can also use the PUE that corresponds with your cloud provider:
You can also use the PUE that corresponds with your cloud provider:
<ul style="list-style-type: none;margin-left: 4px;">
<li><strong>AWS:</strong> 1.135</li>
<li><strong>GCP:</strong> 1.1</li>
@ -444,7 +463,7 @@ export const uiSettings: Record<string, UiSettings> = {
}),
value: 0.000379069,
description: i18n.translate('xpack.observability.profilingCo2PerKWHUiSettingDescription', {
defaultMessage: `Carbon intensity measures how clean your data center electricity is.
defaultMessage: `Carbon intensity measures how clean your data center electricity is.
Specifically, it measures the average amount of CO2 emitted per kilowatt-hour (kWh) of electricity consumed in a particular region.
Use the cloud carbon footprint {datasheetLink} to update this value according to your region. Defaults to US East (N. Virginia).`,
values: {

View file

@ -7,6 +7,7 @@
import moment from 'moment';
import expect from '@kbn/expect';
import { enableInfrastructureProfilingIntegration } from '@kbn/observability-plugin/common';
import { FtrProviderContext } from '../../ftr_provider_context';
import { DATES, NODE_DETAILS_PATH, DATE_PICKER_FORMAT } from './constants';
@ -67,6 +68,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await browser.refresh();
};
const setInfrastructureProfilingIntegrationUiSetting = async (value: boolean = true) => {
await kibanaServer.uiSettings.update({ [enableInfrastructureProfilingIntegration]: value });
await browser.refresh();
await pageObjects.header.waitUntilLoadingHasFinished();
};
describe('Node Details', () => {
describe('#With Asset Details', () => {
before(async () => {
@ -190,6 +197,16 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await pageObjects.header.waitUntilLoadingHasFinished();
await pageObjects.assetDetails.overviewAlertsTitleExists();
});
it('shows the CPU Profiling prompt if UI setting for Profiling integration is enabled', async () => {
await setInfrastructureProfilingIntegrationUiSetting(true);
await pageObjects.assetDetails.cpuProfilingPromptExists();
});
it('hides the CPU Profiling prompt if UI setting for Profiling integration is disabled', async () => {
await setInfrastructureProfilingIntegrationUiSetting(false);
await pageObjects.assetDetails.cpuProfilingPromptMissing();
});
});
describe('Metadata Tab', () => {
@ -312,6 +329,18 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
});
});
describe('Profiling tab', () => {
it('shows the Profiling tab if Profiling integration UI setting is enabled', async () => {
await setInfrastructureProfilingIntegrationUiSetting(true);
await pageObjects.assetDetails.profilingTabExists();
});
it('hides the Profiling tab if Profiling integration UI setting is disabled', async () => {
await setInfrastructureProfilingIntegrationUiSetting(false);
await pageObjects.assetDetails.profilingTabMissing();
});
});
describe('Host with alerts and no processes', () => {
before(async () => {
await navigateToNodeDetails('demo-stack-mysql-01', 'demo-stack-mysql-01');

View file

@ -62,6 +62,22 @@ export function AssetDetailsProvider({ getService }: FtrProviderContext) {
return testSubjects.click('infraAssetDetailsMetadataShowAllButton');
},
async cpuProfilingPromptExists() {
return await testSubjects.existOrFail('infraAssetDetailsCPUProfilingPrompt');
},
async cpuProfilingPromptMissing() {
return await testSubjects.missingOrFail('infraAssetDetailsCPUProfilingPrompt');
},
async profilingTabExists() {
return await testSubjects.existOrFail('infraAssetDetailsProfilingTab');
},
async profilingTabMissing() {
return await testSubjects.missingOrFail('infraAssetDetailsProfilingTab');
},
// Metadata
async clickMetadataTab() {
return testSubjects.click('infraAssetDetailsMetadataTab');