diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md index 33f5775a429be5..5c2c1d53175432 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md @@ -252,6 +252,7 @@ readonly links: { datastreams: string; datastreamsNamingScheme: string; installElasticAgent: string; + installElasticAgentStandalone: string; upgradeElasticAgent: string; upgradeElasticAgent712lower: string; learnMoreBlog: string; diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md index 5a6c7131768bf1..cbfe53d3eaea0a 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md @@ -17,5 +17,5 @@ export interface DocLinksStart | --- | --- | --- | | [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinksstart.doc_link_version.md) | string | | | [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinksstart.elastic_website_url.md) | string | | -| [links](./kibana-plugin-core-public.doclinksstart.links.md) | { readonly settings: string; readonly elasticStackGetStarted: string; readonly upgrade: { readonly upgradingElasticStack: string; }; readonly apm: { readonly kibanaSettings: string; readonly supportedServiceMaps: string; readonly customLinks: string; readonly droppedTransactionSpans: string; readonly upgrading: string; readonly metaData: string; }; readonly canvas: { readonly guide: string; }; readonly dashboard: { readonly guide: string; readonly drilldowns: string; readonly drilldownsTriggerPicker: string; readonly urlDrilldownTemplateSyntax: string; readonly urlDrilldownVariables: string; }; readonly discover: Record<string, string>; readonly filebeat: { readonly base: string; readonly installation: string; readonly configuration: string; readonly elasticsearchOutput: string; readonly elasticsearchModule: string; readonly startup: string; readonly exportedFields: string; readonly suricataModule: string; readonly zeekModule: string; }; readonly auditbeat: { readonly base: string; readonly auditdModule: string; readonly systemModule: string; }; readonly metricbeat: { readonly base: string; readonly configure: string; readonly httpEndpoint: string; readonly install: string; readonly start: string; }; readonly enterpriseSearch: { readonly base: string; readonly appSearchBase: string; readonly workplaceSearchBase: string; }; readonly heartbeat: { readonly base: string; }; readonly libbeat: { readonly getStarted: string; }; readonly logstash: { readonly base: string; }; readonly functionbeat: { readonly base: string; }; readonly winlogbeat: { readonly base: string; }; readonly aggs: { readonly composite: string; readonly composite\_missing\_bucket: string; readonly date\_histogram: string; readonly date\_range: string; readonly date\_format\_pattern: string; readonly filter: string; readonly filters: string; readonly geohash\_grid: string; readonly histogram: string; readonly ip\_range: string; readonly range: string; readonly significant\_terms: string; readonly terms: string; readonly terms\_doc\_count\_error: string; readonly avg: string; readonly avg\_bucket: string; readonly max\_bucket: string; readonly min\_bucket: string; readonly sum\_bucket: string; readonly cardinality: string; readonly count: string; readonly cumulative\_sum: string; readonly derivative: string; readonly geo\_bounds: string; readonly geo\_centroid: string; readonly max: string; readonly median: string; readonly min: string; readonly moving\_avg: string; readonly percentile\_ranks: string; readonly serial\_diff: string; readonly std\_dev: string; readonly sum: string; readonly top\_hits: string; }; readonly runtimeFields: { readonly overview: string; readonly mapping: string; }; readonly scriptedFields: { readonly scriptFields: string; readonly scriptAggs: string; readonly painless: string; readonly painlessApi: string; readonly painlessLangSpec: string; readonly painlessSyntax: string; readonly painlessWalkthrough: string; readonly luceneExpressions: string; }; readonly search: { readonly sessions: string; readonly sessionLimits: string; }; readonly indexPatterns: { readonly introduction: string; readonly fieldFormattersNumber: string; readonly fieldFormattersString: string; readonly runtimeFields: string; }; readonly addData: string; readonly kibana: string; readonly upgradeAssistant: { readonly overview: string; readonly batchReindex: string; readonly remoteReindex: string; }; readonly rollupJobs: string; readonly elasticsearch: Record<string, string>; readonly siem: { readonly privileges: string; readonly guide: string; readonly gettingStarted: string; readonly ml: string; readonly ruleChangeLog: string; readonly detectionsReq: string; readonly networkMap: string; readonly troubleshootGaps: string; }; readonly securitySolution: { readonly trustedApps: string; }; readonly query: { readonly eql: string; readonly kueryQuerySyntax: string; readonly luceneQuerySyntax: string; readonly percolate: string; readonly queryDsl: string; }; readonly date: { readonly dateMath: string; readonly dateMathIndexNames: string; }; readonly management: Record<string, string>; readonly ml: Record<string, string>; readonly transforms: Record<string, string>; readonly visualize: Record<string, string>; readonly apis: Readonly<{ bulkIndexAlias: string; byteSizeUnits: string; createAutoFollowPattern: string; createFollower: string; createIndex: string; createSnapshotLifecyclePolicy: string; createRoleMapping: string; createRoleMappingTemplates: string; createRollupJobsRequest: string; createApiKey: string; createPipeline: string; createTransformRequest: string; cronExpressions: string; executeWatchActionModes: string; indexExists: string; openIndex: string; putComponentTemplate: string; painlessExecute: string; painlessExecuteAPIContexts: string; putComponentTemplateMetadata: string; putSnapshotLifecyclePolicy: string; putIndexTemplateV1: string; putWatch: string; simulatePipeline: string; timeUnits: string; updateTransform: string; }>; readonly observability: Readonly<{ guide: string; infrastructureThreshold: string; logsThreshold: string; metricsThreshold: string; monitorStatus: string; monitorUptime: string; tlsCertificate: string; uptimeDurationAnomaly: string; }>; readonly alerting: Record<string, string>; readonly maps: Readonly<{ guide: string; importGeospatialPrivileges: string; gdalTutorial: string; }>; readonly monitoring: Record<string, string>; readonly security: Readonly<{ apiKeyServiceSettings: string; clusterPrivileges: string; elasticsearchSettings: string; elasticsearchEnableSecurity: string; elasticsearchEnableApiKeys: string; indicesPrivileges: string; kibanaTLS: string; kibanaPrivileges: string; mappingRoles: string; mappingRolesFieldRules: string; runAsPrivilege: string; }>; readonly spaces: Readonly<{ kibanaLegacyUrlAliases: string; kibanaDisableLegacyUrlAliasesApi: string; }>; readonly watcher: Record<string, string>; readonly ccs: Record<string, string>; readonly plugins: Record<string, string>; readonly snapshotRestore: Record<string, string>; readonly ingest: Record<string, string>; readonly fleet: Readonly<{ beatsAgentComparison: string; guide: string; fleetServer: string; fleetServerAddFleetServer: string; settings: string; settingsFleetServerHostSettings: string; settingsFleetServerProxySettings: string; troubleshooting: string; elasticAgent: string; datastreams: string; datastreamsNamingScheme: string; installElasticAgent: string; upgradeElasticAgent: string; upgradeElasticAgent712lower: string; learnMoreBlog: string; apiKeysLearnMore: string; onPremRegistry: string; }>; readonly ecs: { readonly guide: string; }; readonly clients: { readonly guide: string; readonly goOverview: string; readonly javaIndex: string; readonly jsIntro: string; readonly netGuide: string; readonly perlGuide: string; readonly phpGuide: string; readonly pythonGuide: string; readonly rubyOverview: string; readonly rustGuide: string; }; readonly endpoints: { readonly troubleshooting: string; }; } | | +| [links](./kibana-plugin-core-public.doclinksstart.links.md) | { readonly settings: string; readonly elasticStackGetStarted: string; readonly upgrade: { readonly upgradingElasticStack: string; }; readonly apm: { readonly kibanaSettings: string; readonly supportedServiceMaps: string; readonly customLinks: string; readonly droppedTransactionSpans: string; readonly upgrading: string; readonly metaData: string; }; readonly canvas: { readonly guide: string; }; readonly dashboard: { readonly guide: string; readonly drilldowns: string; readonly drilldownsTriggerPicker: string; readonly urlDrilldownTemplateSyntax: string; readonly urlDrilldownVariables: string; }; readonly discover: Record<string, string>; readonly filebeat: { readonly base: string; readonly installation: string; readonly configuration: string; readonly elasticsearchOutput: string; readonly elasticsearchModule: string; readonly startup: string; readonly exportedFields: string; readonly suricataModule: string; readonly zeekModule: string; }; readonly auditbeat: { readonly base: string; readonly auditdModule: string; readonly systemModule: string; }; readonly metricbeat: { readonly base: string; readonly configure: string; readonly httpEndpoint: string; readonly install: string; readonly start: string; }; readonly enterpriseSearch: { readonly base: string; readonly appSearchBase: string; readonly workplaceSearchBase: string; }; readonly heartbeat: { readonly base: string; }; readonly libbeat: { readonly getStarted: string; }; readonly logstash: { readonly base: string; }; readonly functionbeat: { readonly base: string; }; readonly winlogbeat: { readonly base: string; }; readonly aggs: { readonly composite: string; readonly composite\_missing\_bucket: string; readonly date\_histogram: string; readonly date\_range: string; readonly date\_format\_pattern: string; readonly filter: string; readonly filters: string; readonly geohash\_grid: string; readonly histogram: string; readonly ip\_range: string; readonly range: string; readonly significant\_terms: string; readonly terms: string; readonly terms\_doc\_count\_error: string; readonly avg: string; readonly avg\_bucket: string; readonly max\_bucket: string; readonly min\_bucket: string; readonly sum\_bucket: string; readonly cardinality: string; readonly count: string; readonly cumulative\_sum: string; readonly derivative: string; readonly geo\_bounds: string; readonly geo\_centroid: string; readonly max: string; readonly median: string; readonly min: string; readonly moving\_avg: string; readonly percentile\_ranks: string; readonly serial\_diff: string; readonly std\_dev: string; readonly sum: string; readonly top\_hits: string; }; readonly runtimeFields: { readonly overview: string; readonly mapping: string; }; readonly scriptedFields: { readonly scriptFields: string; readonly scriptAggs: string; readonly painless: string; readonly painlessApi: string; readonly painlessLangSpec: string; readonly painlessSyntax: string; readonly painlessWalkthrough: string; readonly luceneExpressions: string; }; readonly search: { readonly sessions: string; readonly sessionLimits: string; }; readonly indexPatterns: { readonly introduction: string; readonly fieldFormattersNumber: string; readonly fieldFormattersString: string; readonly runtimeFields: string; }; readonly addData: string; readonly kibana: string; readonly upgradeAssistant: { readonly overview: string; readonly batchReindex: string; readonly remoteReindex: string; }; readonly rollupJobs: string; readonly elasticsearch: Record<string, string>; readonly siem: { readonly privileges: string; readonly guide: string; readonly gettingStarted: string; readonly ml: string; readonly ruleChangeLog: string; readonly detectionsReq: string; readonly networkMap: string; readonly troubleshootGaps: string; }; readonly securitySolution: { readonly trustedApps: string; }; readonly query: { readonly eql: string; readonly kueryQuerySyntax: string; readonly luceneQuerySyntax: string; readonly percolate: string; readonly queryDsl: string; }; readonly date: { readonly dateMath: string; readonly dateMathIndexNames: string; }; readonly management: Record<string, string>; readonly ml: Record<string, string>; readonly transforms: Record<string, string>; readonly visualize: Record<string, string>; readonly apis: Readonly<{ bulkIndexAlias: string; byteSizeUnits: string; createAutoFollowPattern: string; createFollower: string; createIndex: string; createSnapshotLifecyclePolicy: string; createRoleMapping: string; createRoleMappingTemplates: string; createRollupJobsRequest: string; createApiKey: string; createPipeline: string; createTransformRequest: string; cronExpressions: string; executeWatchActionModes: string; indexExists: string; openIndex: string; putComponentTemplate: string; painlessExecute: string; painlessExecuteAPIContexts: string; putComponentTemplateMetadata: string; putSnapshotLifecyclePolicy: string; putIndexTemplateV1: string; putWatch: string; simulatePipeline: string; timeUnits: string; updateTransform: string; }>; readonly observability: Readonly<{ guide: string; infrastructureThreshold: string; logsThreshold: string; metricsThreshold: string; monitorStatus: string; monitorUptime: string; tlsCertificate: string; uptimeDurationAnomaly: string; }>; readonly alerting: Record<string, string>; readonly maps: Readonly<{ guide: string; importGeospatialPrivileges: string; gdalTutorial: string; }>; readonly monitoring: Record<string, string>; readonly security: Readonly<{ apiKeyServiceSettings: string; clusterPrivileges: string; elasticsearchSettings: string; elasticsearchEnableSecurity: string; elasticsearchEnableApiKeys: string; indicesPrivileges: string; kibanaTLS: string; kibanaPrivileges: string; mappingRoles: string; mappingRolesFieldRules: string; runAsPrivilege: string; }>; readonly spaces: Readonly<{ kibanaLegacyUrlAliases: string; kibanaDisableLegacyUrlAliasesApi: string; }>; readonly watcher: Record<string, string>; readonly ccs: Record<string, string>; readonly plugins: Record<string, string>; readonly snapshotRestore: Record<string, string>; readonly ingest: Record<string, string>; readonly fleet: Readonly<{ beatsAgentComparison: string; guide: string; fleetServer: string; fleetServerAddFleetServer: string; settings: string; settingsFleetServerHostSettings: string; settingsFleetServerProxySettings: string; troubleshooting: string; elasticAgent: string; datastreams: string; datastreamsNamingScheme: string; installElasticAgent: string; installElasticAgentStandalone: string; upgradeElasticAgent: string; upgradeElasticAgent712lower: string; learnMoreBlog: string; apiKeysLearnMore: string; onPremRegistry: string; }>; readonly ecs: { readonly guide: string; }; readonly clients: { readonly guide: string; readonly goOverview: string; readonly javaIndex: string; readonly jsIntro: string; readonly netGuide: string; readonly perlGuide: string; readonly phpGuide: string; readonly pythonGuide: string; readonly rubyOverview: string; readonly rustGuide: string; }; readonly endpoints: { readonly troubleshooting: string; }; } | | diff --git a/package.json b/package.json index df39dd8c65d7e9..983dddda5d4fbe 100644 --- a/package.json +++ b/package.json @@ -222,7 +222,7 @@ "deep-freeze-strict": "^1.1.1", "deepmerge": "^4.2.2", "del": "^5.1.0", - "elastic-apm-node": "^3.24.0", + "elastic-apm-node": "^3.25.0", "execa": "^4.0.2", "exit-hook": "^2.2.0", "expiry-js": "0.1.7", diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index ec7802b68e7c55..92b4c815f22492 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -498,6 +498,7 @@ export class DocLinksService { datastreams: `${FLEET_DOCS}data-streams.html`, datastreamsNamingScheme: `${FLEET_DOCS}data-streams.html#data-streams-naming-scheme`, installElasticAgent: `${FLEET_DOCS}install-fleet-managed-elastic-agent.html`, + installElasticAgentStandalone: `${FLEET_DOCS}install-standalone-elastic-agent.html`, upgradeElasticAgent: `${FLEET_DOCS}upgrade-elastic-agent.html`, upgradeElasticAgent712lower: `${FLEET_DOCS}upgrade-elastic-agent.html#upgrade-7.12-lower`, learnMoreBlog: `${ELASTIC_WEBSITE_URL}blog/elastic-agent-and-fleet-make-it-easier-to-integrate-your-systems-with-elastic`, @@ -777,6 +778,7 @@ export interface DocLinksStart { datastreams: string; datastreamsNamingScheme: string; installElasticAgent: string; + installElasticAgentStandalone: string; upgradeElasticAgent: string; upgradeElasticAgent712lower: string; learnMoreBlog: string; diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 5dcd3422d5d86c..772faa5321d982 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -734,6 +734,7 @@ export interface DocLinksStart { datastreams: string; datastreamsNamingScheme: string; installElasticAgent: string; + installElasticAgentStandalone: string; upgradeElasticAgent: string; upgradeElasticAgent712lower: string; learnMoreBlog: string; diff --git a/src/core/server/saved_objects/migrations/integration_tests/7_13_0_failed_action_tasks.test.ts b/src/core/server/saved_objects/migrations/integration_tests/7_13_0_failed_action_tasks.test.ts index 479b1e78e1b720..2def8e375c81f5 100644 --- a/src/core/server/saved_objects/migrations/integration_tests/7_13_0_failed_action_tasks.test.ts +++ b/src/core/server/saved_objects/migrations/integration_tests/7_13_0_failed_action_tasks.test.ts @@ -19,7 +19,8 @@ async function removeLogFile() { await fs.unlink(logFilePath).catch(() => void 0); } -describe('migration from 7.13 to 7.14+ with many failed action_tasks', () => { +// FLAKY: https://github.com/elastic/kibana/issues/118626 +describe.skip('migration from 7.13 to 7.14+ with many failed action_tasks', () => { let esServer: kbnTestServer.TestElasticsearchUtils; let root: Root; let startES: () => Promise; diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor_output.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor_output.tsx index 23c1c515cc9fc5..cb5301fa41c1d8 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor_output.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor_output.tsx @@ -28,6 +28,9 @@ import { applyCurrentSettings } from './apply_editor_settings'; const isJSONContentType = (contentType?: string) => Boolean(contentType && contentType.indexOf('application/json') >= 0); +const isMapboxVectorTile = (contentType?: string) => + contentType?.includes('application/vnd.mapbox-vector-tile') ?? false; + /** * Best effort expand literal strings */ @@ -85,6 +88,11 @@ function EditorOutputUI() { if (readOnlySettings.tripleQuotes && isJSONContentType(contentType)) { return safeExpandLiteralStrings(value as string); } + if (isMapboxVectorTile(contentType)) { + return i18n.translate('console.outputCannotPreviewBinaryData', { + defaultMessage: 'Cannot preview binary data.', + }); + } return value; }) .join('\n'), diff --git a/src/plugins/console/public/types/common.ts b/src/plugins/console/public/types/common.ts index 53d896ad01d2f6..79013ce312726c 100644 --- a/src/plugins/console/public/types/common.ts +++ b/src/plugins/console/public/types/common.ts @@ -23,4 +23,5 @@ export type BaseResponseType = | 'text/tab-separated-values' | 'text/plain' | 'application/yaml' - | 'unknown'; + | 'unknown' + | 'application/vnd.mapbox-vector-tile'; diff --git a/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx b/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx index 3dd8c8ec656146..edbaa22869b33d 100644 --- a/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx +++ b/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx @@ -63,7 +63,6 @@ const SavedObjectsTablePage = ({ text: i18n.translate('savedObjectsManagement.breadcrumb.index', { defaultMessage: 'Saved objects', }), - href: '/', }, ]); }, [setBreadcrumbs]); diff --git a/src/plugins/vis_types/timelion/common/types.ts b/src/plugins/vis_types/timelion/common/types.ts index 8ce4bd8b45f0d7..323539f69488a3 100644 --- a/src/plugins/vis_types/timelion/common/types.ts +++ b/src/plugins/vis_types/timelion/common/types.ts @@ -20,6 +20,7 @@ export interface TimelionFunctionArgs { multi?: boolean; types: TimelionFunctionArgsTypes[]; suggestions?: TimelionFunctionArgsSuggestion[]; + hidden?: boolean; } export interface ITimelionFunction { diff --git a/src/plugins/vis_types/timelion/public/components/timelion_expression_input_helpers.ts b/src/plugins/vis_types/timelion/public/components/timelion_expression_input_helpers.ts index 6c3cd8058627af..676b5d91a803f8 100644 --- a/src/plugins/vis_types/timelion/public/components/timelion_expression_input_helpers.ts +++ b/src/plugins/vis_types/timelion/public/components/timelion_expression_input_helpers.ts @@ -42,7 +42,7 @@ function getArgumentsHelp( // ignore arguments that are already provided in function declaration const functionArgNames = functionArgs.map((arg) => arg.name); - return argsHelp.filter((arg) => !functionArgNames.includes(arg.name)); + return argsHelp.filter((arg) => !arg.hidden && !functionArgNames.includes(arg.name)); } async function extractSuggestionsFromParsedResult( diff --git a/src/plugins/vis_types/timelion/server/lib/classes/datasource.js b/src/plugins/vis_types/timelion/server/lib/classes/datasource.js index f0af22793c98f2..50129494bf84cd 100644 --- a/src/plugins/vis_types/timelion/server/lib/classes/datasource.js +++ b/src/plugins/vis_types/timelion/server/lib/classes/datasource.js @@ -47,6 +47,7 @@ export default class Datasource extends TimelionFunction { fitFunctions: _.keys(fitFunctions).join(', '), }, }), + hidden: Boolean(config.hideFitArg), }); // Wrap the original function so we can modify inputs/outputs with offset & fit diff --git a/src/plugins/vis_types/timelion/server/series_functions/es/index.js b/src/plugins/vis_types/timelion/server/series_functions/es/index.js index 663d7714774c2f..d613818d7c3e3c 100644 --- a/src/plugins/vis_types/timelion/server/series_functions/es/index.js +++ b/src/plugins/vis_types/timelion/server/series_functions/es/index.js @@ -13,6 +13,7 @@ import buildRequest from './lib/build_request'; import toSeriesList from './lib/agg_response_to_series_list'; export default new Datasource('es', { + hideFitArg: true, args: [ { name: 'q', diff --git a/src/plugins/vis_types/vega/public/components/vega_vis_component.tsx b/src/plugins/vis_types/vega/public/components/vega_vis_component.tsx index c9d89abcf265ce..2639f39abd0993 100644 --- a/src/plugins/vis_types/vega/public/components/vega_vis_component.tsx +++ b/src/plugins/vis_types/vega/public/components/vega_vis_component.tsx @@ -10,7 +10,7 @@ import React, { useEffect, useCallback, useRef } from 'react'; import { EuiResizeObserver } from '@elastic/eui'; import { throttle } from 'lodash'; -import { IInterpreterRenderHandlers } from 'src/plugins/expressions'; +import type { IInterpreterRenderHandlers, RenderMode } from 'src/plugins/expressions'; import { createVegaVisualization } from '../vega_visualization'; import { VegaVisualizationDependencies } from '../plugin'; import { VegaParser } from '../data_model/vega_parser'; @@ -21,18 +21,25 @@ interface VegaVisComponentProps { deps: VegaVisualizationDependencies; fireEvent: IInterpreterRenderHandlers['event']; renderComplete: () => void; + renderMode: RenderMode; visData: VegaParser; } type VegaVisController = InstanceType>; -const VegaVisComponent = ({ visData, fireEvent, renderComplete, deps }: VegaVisComponentProps) => { +const VegaVisComponent = ({ + visData, + fireEvent, + renderComplete, + deps, + renderMode, +}: VegaVisComponentProps) => { const chartDiv = useRef(null); const visController = useRef(null); useEffect(() => { if (chartDiv.current) { - const VegaVis = createVegaVisualization(deps); + const VegaVis = createVegaVisualization(deps, renderMode); visController.current = new VegaVis(chartDiv.current, fireEvent); } @@ -40,7 +47,7 @@ const VegaVisComponent = ({ visData, fireEvent, renderComplete, deps }: VegaVisC visController.current?.destroy(); visController.current = null; }; - }, [deps, fireEvent]); + }, [deps, fireEvent, renderMode]); useEffect(() => { if (visController.current) { diff --git a/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js b/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js index 8c725ba0a75a26..4485e2ed855f5a 100644 --- a/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js +++ b/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js @@ -89,6 +89,7 @@ export class VegaBaseView { this._initialized = false; this._externalUrl = opts.externalUrl; this._enableExternalUrls = getEnableExternalUrls(); + this._renderMode = opts.renderMode; this._vegaStateRestorer = opts.vegaStateRestorer; } @@ -238,7 +239,7 @@ export class VegaBaseView { } onWarn() { - if (!this._parser || !this._parser.hideWarnings) { + if (this._renderMode !== 'view' && (!this._parser || !this._parser.hideWarnings)) { this._addMessage('warn', Utils.formatWarningToStr(...arguments)); } } diff --git a/src/plugins/vis_types/vega/public/vega_vis_renderer.tsx b/src/plugins/vis_types/vega/public/vega_vis_renderer.tsx index e6a39f5a18e837..b2f10c37c79053 100644 --- a/src/plugins/vis_types/vega/public/vega_vis_renderer.tsx +++ b/src/plugins/vis_types/vega/public/vega_vis_renderer.tsx @@ -33,6 +33,7 @@ export const getVegaVisRenderer: ( deps={deps} fireEvent={handlers.event} renderComplete={handlers.done} + renderMode={handlers.getRenderMode()} visData={visData} /> diff --git a/src/plugins/vis_types/vega/public/vega_visualization.ts b/src/plugins/vis_types/vega/public/vega_visualization.ts index 556f80cc3ebeb3..2ab55a0358f4df 100644 --- a/src/plugins/vis_types/vega/public/vega_visualization.ts +++ b/src/plugins/vis_types/vega/public/vega_visualization.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import { IInterpreterRenderHandlers } from 'src/plugins/expressions'; +import type { IInterpreterRenderHandlers, RenderMode } from 'src/plugins/expressions'; import { VegaParser } from './data_model/vega_parser'; import { VegaVisualizationDependencies } from './plugin'; import { getNotifications, getData } from './services'; @@ -19,10 +19,10 @@ type VegaVisType = new (el: HTMLDivElement, fireEvent: IInterpreterRenderHandler destroy(): void; }; -export const createVegaVisualization = ({ - core, - getServiceSettings, -}: VegaVisualizationDependencies): VegaVisType => +export const createVegaVisualization = ( + { core, getServiceSettings }: VegaVisualizationDependencies, + renderMode: RenderMode +): VegaVisType => class VegaVisualization { private readonly dataPlugin = getData(); private vegaView: InstanceType | null = null; @@ -82,6 +82,7 @@ export const createVegaVisualization = ({ serviceSettings, filterManager, timefilter, + renderMode, }; if (vegaParser.useMap) { diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx index 3a248732eac0c3..b87065a815410c 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx @@ -46,6 +46,7 @@ import { SavedObjectAttributes } from '../../../../core/types'; import { getSavedVisualization } from '../utils/saved_visualize_utils'; import { VisSavedObject } from '../types'; import { toExpressionAst } from './to_ast'; +import type { RenderMode } from '../../../expressions'; const getKeys = (o: T): Array => Object.keys(o) as Array; @@ -63,6 +64,7 @@ export interface VisualizeInput extends EmbeddableInput { colors?: { [key: string]: string }; }; savedVis?: SerializedVis; + renderMode?: RenderMode; table?: unknown; query?: Query; filters?: Filter[]; @@ -314,6 +316,7 @@ export class VisualizeEmbeddable const expressions = getExpressions(); this.handler = await expressions.loader(this.domNode, undefined, { + renderMode: this.input.renderMode || 'view', onRenderError: (element: HTMLElement, error: ExpressionRenderError) => { this.onContainerError(error); }, diff --git a/src/plugins/visualize/public/application/utils/get_visualization_instance.test.ts b/src/plugins/visualize/public/application/utils/get_visualization_instance.test.ts index 777ba244c06a11..ce8fd951d7a139 100644 --- a/src/plugins/visualize/public/application/utils/get_visualization_instance.test.ts +++ b/src/plugins/visualize/public/application/utils/get_visualization_instance.test.ts @@ -87,8 +87,10 @@ describe('getVisualizationInstance', () => { serializedVisMock ); expect(mockServices.createVisEmbeddableFromObject).toHaveBeenCalledWith(visMock, { + searchSessionId: undefined, timeRange: undefined, filters: undefined, + renderMode: 'edit', id: '', }); @@ -203,8 +205,10 @@ describe('getVisualizationInstanceInput', () => { input.savedVis ); expect(mockServices.createVisEmbeddableFromObject).toHaveBeenCalledWith(visMock, { + searchSessionId: undefined, timeRange: undefined, filters: undefined, + renderMode: 'edit', id: '', }); diff --git a/src/plugins/visualize/public/application/utils/get_visualization_instance.ts b/src/plugins/visualize/public/application/utils/get_visualization_instance.ts index 876501d5f099b9..bc513844089357 100644 --- a/src/plugins/visualize/public/application/utils/get_visualization_instance.ts +++ b/src/plugins/visualize/public/application/utils/get_visualization_instance.ts @@ -42,6 +42,7 @@ const createVisualizeEmbeddableAndLinkSavedSearch = async ( timeRange: data.query.timefilter.timefilter.getTime(), filters: data.query.filterManager.getFilters(), searchSessionId: data.search.session.getSessionId(), + renderMode: 'edit', })) as VisualizeEmbeddableContract; embeddableHandler.getOutput$().subscribe((output) => { diff --git a/test/functional/apps/management/_scripted_fields_preview.js b/test/functional/apps/management/_scripted_fields_preview.js index 12a6cb9537c8d6..a442b521d5d989 100644 --- a/test/functional/apps/management/_scripted_fields_preview.js +++ b/test/functional/apps/management/_scripted_fields_preview.js @@ -13,7 +13,8 @@ export default function ({ getService, getPageObjects }) { const PageObjects = getPageObjects(['settings']); const SCRIPTED_FIELD_NAME = 'myScriptedField'; - describe('scripted fields preview', () => { + // FLAKY: https://github.com/elastic/kibana/issues/118981 + describe.skip('scripted fields preview', () => { before(async function () { await browser.setWindowSize(1200, 800); await PageObjects.settings.navigateTo(); diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index 518d4582de2bc7..9458180fdd2200 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -105,7 +105,7 @@ export class ActionExecutor { name: `execute_action`, type: 'actions', labels: { - actionId, + actions_connector_id: actionId, }, }, async (span) => { @@ -135,7 +135,7 @@ export class ActionExecutor { if (span) { span.name = `execute_action ${actionTypeId}`; span.addLabels({ - actionTypeId, + actions_connector_type_id: actionTypeId, }); } diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index f651f41ef0c1e3..fe95ec646387d7 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import apm from 'elastic-apm-node'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { Dictionary, pickBy, mapValues, without, cloneDeep } from 'lodash'; import type { Request } from '@hapi/hapi'; @@ -529,6 +529,17 @@ export class TaskRunner< // Ensure API key is still valid and user has access try { alert = await rulesClient.get({ id: alertId }); + + if (apm.currentTransaction) { + apm.currentTransaction.name = `Execute Alerting Rule: "${alert.name}"`; + apm.currentTransaction.addLabels({ + alerting_rule_consumer: alert.consumer, + alerting_rule_name: alert.name, + alerting_rule_tags: alert.tags.join(', '), + alerting_rule_type_id: alert.alertTypeId, + alerting_rule_params: JSON.stringify(alert.params), + }); + } } catch (err) { throw new ErrorWithReason(AlertExecutionStatusErrorReasons.Read, err); } @@ -560,6 +571,13 @@ export class TaskRunner< schedule: taskSchedule, } = this.taskInstance; + if (apm.currentTransaction) { + apm.currentTransaction.name = `Execute Alerting Rule`; + apm.currentTransaction.addLabels({ + alerting_rule_id: alertId, + }); + } + const runDate = new Date(); const runDateString = runDate.toISOString(); this.logger.debug(`executing alert ${this.alertType.id}:${alertId} at ${runDateString}`); @@ -615,6 +633,14 @@ export class TaskRunner< executionStatus.lastExecutionDate = new Date(event.event.start); } + if (apm.currentTransaction) { + if (executionStatus.status === 'ok' || executionStatus.status === 'active') { + apm.currentTransaction.setOutcome('success'); + } else if (executionStatus.status === 'error' || executionStatus.status === 'unknown') { + apm.currentTransaction.setOutcome('failure'); + } + } + this.logger.debug( `alertExecutionStatus for ${this.alertType.id}:${alertId}: ${JSON.stringify(executionStatus)}` ); @@ -855,6 +881,12 @@ function generateNewAndRecoveredInstanceEvents< const recoveredAlertInstanceIds = Object.keys(recoveredAlertInstances); const newIds = without(currentAlertInstanceIds, ...originalAlertInstanceIds); + if (apm.currentTransaction) { + apm.currentTransaction.addLabels({ + alerting_new_alerts: newIds.length, + }); + } + for (const id of recoveredAlertInstanceIds) { const { group: actionGroup, subgroup: actionSubgroup } = recoveredAlertInstances[id].getLastScheduledActions() ?? {}; @@ -1035,6 +1067,14 @@ function logActiveAndRecoveredInstances< const { logger, activeAlertInstances, recoveredAlertInstances, alertLabel } = params; const activeInstanceIds = Object.keys(activeAlertInstances); const recoveredInstanceIds = Object.keys(recoveredAlertInstances); + + if (apm.currentTransaction) { + apm.currentTransaction.addLabels({ + alerting_active_alerts: activeInstanceIds.length, + alerting_recovered_alerts: recoveredInstanceIds.length, + }); + } + if (activeInstanceIds.length > 0) { logger.debug( `alert ${alertLabel} has ${activeInstanceIds.length} active alert instances: ${JSON.stringify( diff --git a/x-pack/plugins/fleet/common/constants/epm.ts b/x-pack/plugins/fleet/common/constants/epm.ts index 5137e422e09755..97672f4d4d657a 100644 --- a/x-pack/plugins/fleet/common/constants/epm.ts +++ b/x-pack/plugins/fleet/common/constants/epm.ts @@ -18,7 +18,8 @@ export const FLEET_SYNTHETICS_PACKAGE = 'synthetics'; export const FLEET_KUBERNETES_PACKAGE = 'kubernetes'; export const KUBERNETES_RUN_INSTRUCTIONS = 'kubectl apply -f elastic-agent-standalone-kubernetes.yaml'; -export const STANDALONE_RUN_INSTRUCTIONS = './elastic-agent install'; +export const STANDALONE_RUN_INSTRUCTIONS_LINUXMAC = 'sudo ./elastic-agent install'; +export const STANDALONE_RUN_INSTRUCTIONS_WINDOWS = '.\\elastic-agent.exe install'; /* Package rules: diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/standalone_instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/standalone_instructions.tsx index f1ab855059c3cd..4e5f17509fb2d7 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/standalone_instructions.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/standalone_instructions.tsx @@ -36,9 +36,12 @@ import type { PackagePolicy } from '../../../common'; import { FLEET_KUBERNETES_PACKAGE, KUBERNETES_RUN_INSTRUCTIONS, - STANDALONE_RUN_INSTRUCTIONS, + STANDALONE_RUN_INSTRUCTIONS_LINUXMAC, + STANDALONE_RUN_INSTRUCTIONS_WINDOWS, } from '../../../common'; +import { PlatformSelector } from '../enrollment_instructions/manual/platform_selector'; + import { DownloadStep, AgentPolicySelectionStep } from './steps'; import type { BaseProps } from './types'; @@ -55,8 +58,11 @@ export const StandaloneInstructions = React.memo(({ agentPolicy, agentPol 'IS_LOADING' ); const [yaml, setYaml] = useState(''); - const runInstructions = - isK8s === 'IS_KUBERNETES' ? KUBERNETES_RUN_INSTRUCTIONS : STANDALONE_RUN_INSTRUCTIONS; + const linuxMacCommand = + isK8s === 'IS_KUBERNETES' ? KUBERNETES_RUN_INSTRUCTIONS : STANDALONE_RUN_INSTRUCTIONS_LINUXMAC; + const windowsCommand = + isK8s === 'IS_KUBERNETES' ? KUBERNETES_RUN_INSTRUCTIONS : STANDALONE_RUN_INSTRUCTIONS_WINDOWS; + const { docLinks } = useStartServices(); useEffect(() => { async function checkifK8s() { @@ -172,19 +178,6 @@ export const StandaloneInstructions = React.memo(({ agentPolicy, agentPol /> ); - const applyMsg = - isK8s === 'IS_KUBERNETES' ? ( - - ) : ( - - ); - const steps = [ !agentPolicy ? AgentPolicySelectionStep({ agentPolicies, setSelectedPolicyId, excludeFleetServer: true }) @@ -231,24 +224,13 @@ export const StandaloneInstructions = React.memo(({ agentPolicy, agentPol defaultMessage: 'Start the agent', }), children: ( - <> - - <>{applyMsg} - - {runInstructions} - - - {(copy) => ( - - - - )} - - - + ), }, { diff --git a/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx b/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx index 574a04dfd54df8..32846620221aed 100644 --- a/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx +++ b/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx @@ -6,25 +6,15 @@ */ import React from 'react'; -import styled from 'styled-components'; -import { EuiText, EuiSpacer, EuiLink, EuiCodeBlock, EuiButtonGroup } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { i18n } from '@kbn/i18n'; - +import { useStartServices } from '../../../hooks'; import type { EnrollmentAPIKey } from '../../../types'; -import { PLATFORM_OPTIONS, usePlatform, useStartServices } from '../../../hooks'; -import type { PLATFORM_TYPE } from '../../../hooks'; +import { PlatformSelector } from './platform_selector'; interface Props { fleetServerHosts: string[]; apiKey: EnrollmentAPIKey; } -// Otherwise the copy button is over the text -const CommandCode = styled.pre({ - overflow: 'scroll', -}); - function getfleetServerHostsEnrollArgs(apiKey: EnrollmentAPIKey, fleetServerHosts: string[]) { return `--url=${fleetServerHosts[0]} --enrollment-token=${apiKey.api_key}`; } @@ -33,9 +23,7 @@ export const ManualInstructions: React.FunctionComponent = ({ apiKey, fleetServerHosts, }) => { - const { platform, setPlatform } = usePlatform(); const { docLinks } = useStartServices(); - const enrollArgs = getfleetServerHostsEnrollArgs(apiKey, fleetServerHosts); const linuxMacCommand = `sudo ./elastic-agent install ${enrollArgs}`; @@ -43,70 +31,12 @@ export const ManualInstructions: React.FunctionComponent = ({ const windowsCommand = `.\\elastic-agent.exe install ${enrollArgs}`; return ( - <> - - - - - setPlatform(id as PLATFORM_TYPE)} - legend={i18n.translate('xpack.fleet.enrollmentInstructions.platformSelectAriaLabel', { - defaultMessage: 'Platform', - })} - /> - - {platform === 'linux-mac' && ( - - {linuxMacCommand} - - )} - {platform === 'windows' && ( - - {windowsCommand} - - )} - - {platform === 'rpm-deb' && ( - - - - - ), - }} - /> - - )} - - - - - - - ), - }} - /> - - + ); }; diff --git a/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/platform_selector.tsx b/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/platform_selector.tsx new file mode 100644 index 00000000000000..315c5b78bb3eda --- /dev/null +++ b/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/platform_selector.tsx @@ -0,0 +1,119 @@ +/* + * 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 React from 'react'; +import styled from 'styled-components'; +import { EuiText, EuiSpacer, EuiLink, EuiCodeBlock, EuiButtonGroup } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; + +import type { PLATFORM_TYPE } from '../../../hooks'; +import { PLATFORM_OPTIONS, usePlatform } from '../../../hooks'; + +interface Props { + linuxMacCommand: string; + windowsCommand: string; + installAgentLink: string; + troubleshootLink: string; + isK8s: boolean; +} + +// Otherwise the copy button is over the text +const CommandCode = styled.pre({ + overflow: 'auto', +}); + +export const PlatformSelector: React.FunctionComponent = ({ + linuxMacCommand, + windowsCommand, + installAgentLink, + troubleshootLink, + isK8s, +}) => { + const { platform, setPlatform } = usePlatform(); + + return ( + <> + + {isK8s ? ( + + ) : ( + + )} + + + {isK8s ? ( + + {linuxMacCommand} + + ) : ( + <> + setPlatform(id as PLATFORM_TYPE)} + legend={i18n.translate('xpack.fleet.enrollmentInstructions.platformSelectAriaLabel', { + defaultMessage: 'Platform', + })} + /> + + {platform === 'linux-mac' && ( + + {linuxMacCommand} + + )} + {platform === 'windows' && ( + + {windowsCommand} + + )} + {platform === 'rpm-deb' && ( + + + + + ), + }} + /> + + )} + + )} + + + + + + ), + }} + /> + + + ); +}; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_modal.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_modal.tsx index 6767ee439ca615..3d22ed72487f5c 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_modal.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_modal.tsx @@ -28,6 +28,7 @@ export const DeleteActionModal: FC = ({ toggleDeleteIndex, toggleDeleteIndexPattern, userCanDeleteIndex, + userCanDeleteDataView, }) => { if (item === undefined) { return null; @@ -85,6 +86,7 @@ export const DeleteActionModal: FC = ({ })} checked={deleteIndexPattern} onChange={toggleDeleteIndexPattern} + disabled={userCanDeleteDataView === false} /> )} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx index bed9a2bbc018a8..64d91b98973145 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx @@ -42,11 +42,13 @@ export const useDeleteAction = (canDeleteDataFrameAnalytics: boolean) => { const [deleteTargetIndex, setDeleteTargetIndex] = useState(true); const [deleteIndexPattern, setDeleteIndexPattern] = useState(true); const [userCanDeleteIndex, setUserCanDeleteIndex] = useState(false); + const [userCanDeleteDataView, setUserCanDeleteDataView] = useState(false); const [indexPatternExists, setIndexPatternExists] = useState(false); const [isLoading, setIsLoading] = useState(false); const { data: { dataViews }, + application: { capabilities }, } = useMlKibana().services; const indexName = item?.config.dest.index ?? ''; @@ -83,6 +85,14 @@ export const useDeleteAction = (canDeleteDataFrameAnalytics: boolean) => { if (userCanDelete) { setUserCanDeleteIndex(true); } + + const canDeleteDataView = + capabilities.savedObjectsManagement.delete === true || + capabilities.indexPatterns.save === true; + setUserCanDeleteDataView(canDeleteDataView); + if (canDeleteDataView === false) { + setDeleteIndexPattern(false); + } } catch (e) { const error = extractErrorMessage(e); setIsLoading(false); @@ -180,5 +190,6 @@ export const useDeleteAction = (canDeleteDataFrameAnalytics: boolean) => { toggleDeleteIndex, toggleDeleteIndexPattern, userCanDeleteIndex, + userCanDeleteDataView, }; }; diff --git a/x-pack/plugins/ml/server/lib/data_views_utils.ts b/x-pack/plugins/ml/server/lib/data_views_utils.ts index 497404425eff83..b76765c85d8860 100644 --- a/x-pack/plugins/ml/server/lib/data_views_utils.ts +++ b/x-pack/plugins/ml/server/lib/data_views_utils.ts @@ -22,5 +22,5 @@ export function getDataViewsServiceFactory( throw Error('data views service has not been initialized'); } - return () => dataViews.dataViewsServiceFactory(savedObjectClient, scopedClient.asInternalUser); + return () => dataViews.dataViewsServiceFactory(savedObjectClient, scopedClient.asCurrentUser); } diff --git a/x-pack/plugins/monitoring/public/alerts/badge.tsx b/x-pack/plugins/monitoring/public/alerts/badge.tsx index 22bffb5d62b195..6ccca9278edc0a 100644 --- a/x-pack/plugins/monitoring/public/alerts/badge.tsx +++ b/x-pack/plugins/monitoring/public/alerts/badge.tsx @@ -97,6 +97,7 @@ export const AlertsBadge: React.FC = (props: Props) => { { })}

), + 'data-test-subj': 'alertsCreatedToast', }); }; diff --git a/x-pack/plugins/monitoring/public/components/renderers/__snapshots__/setup_mode.test.js.snap b/x-pack/plugins/monitoring/public/components/renderers/__snapshots__/setup_mode.test.js.snap index 8134143ca53204..b5d37c2c2afaec 100644 --- a/x-pack/plugins/monitoring/public/components/renderers/__snapshots__/setup_mode.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/renderers/__snapshots__/setup_mode.test.js.snap @@ -78,6 +78,7 @@ exports[`SetupModeRenderer should render the flyout open 1`] = ` > toggleSetupMode(false)} + data-test-subj="exitSetupModeBtn" > {i18n.translate('xpack.monitoring.setupMode.exit', { defaultMessage: `Exit setup mode`, diff --git a/x-pack/plugins/monitoring/public/components/setup_mode/__snapshots__/enter_button.test.tsx.snap b/x-pack/plugins/monitoring/public/components/setup_mode/__snapshots__/enter_button.test.tsx.snap index 0d9e50d14657b9..0cf078d260ec7a 100644 --- a/x-pack/plugins/monitoring/public/components/setup_mode/__snapshots__/enter_button.test.tsx.snap +++ b/x-pack/plugins/monitoring/public/components/setup_mode/__snapshots__/enter_button.test.tsx.snap @@ -3,9 +3,9 @@ exports[`EnterButton should render properly 1`] = `
= ( } return ( -
+
{i18n.translate('xpack.monitoring.setupMode.enter', { defaultMessage: 'Enter setup mode', diff --git a/x-pack/plugins/saved_objects_tagging/public/management/tag_management_page.tsx b/x-pack/plugins/saved_objects_tagging/public/management/tag_management_page.tsx index f93a9541db0399..bbc018f8e12b08 100644 --- a/x-pack/plugins/saved_objects_tagging/public/management/tag_management_page.tsx +++ b/x-pack/plugins/saved_objects_tagging/public/management/tag_management_page.tsx @@ -122,7 +122,6 @@ export const TagManagementPage: FC = ({ text: i18n.translate('xpack.savedObjectsTagging.management.breadcrumb.index', { defaultMessage: 'Tags', }), - href: '/', }, ]); }, [setBreadcrumbs]); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx index 0c500ee805a8f1..235cdd9c740eed 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx @@ -189,7 +189,7 @@ export const getColumns = ({ align: 'center', render: (tags: Rule['tags']) => { if (tags.length === 0) { - return getEmptyTagValue(); + return null; } const renderItem = (tag: string, i: number) => ( diff --git a/x-pack/plugins/task_manager/server/task_scheduling.test.ts b/x-pack/plugins/task_manager/server/task_scheduling.test.ts index 41a172bfb2f8ed..f593363c53bcf2 100644 --- a/x-pack/plugins/task_manager/server/task_scheduling.test.ts +++ b/x-pack/plugins/task_manager/server/task_scheduling.test.ts @@ -35,6 +35,9 @@ jest.mock('uuid', () => ({ jest.mock('elastic-apm-node', () => ({ currentTraceparent: 'parent', + currentTransaction: { + type: 'taskManager run', + }, })); describe('TaskScheduling', () => { diff --git a/x-pack/plugins/task_manager/server/task_scheduling.ts b/x-pack/plugins/task_manager/server/task_scheduling.ts index a89f66d9c772bf..abf1ea0f50eda5 100644 --- a/x-pack/plugins/task_manager/server/task_scheduling.ts +++ b/x-pack/plugins/task_manager/server/task_scheduling.ts @@ -99,9 +99,15 @@ export class TaskScheduling { ...options, taskInstance: ensureDeprecatedFieldsAreCorrected(taskInstance, this.logger), }); + + const traceparent = + agent.currentTransaction && agent.currentTransaction.type !== 'request' + ? agent.currentTraceparent + : ''; + return await this.store.schedule({ ...modifiedTask, - traceparent: agent.currentTraceparent ?? '', + traceparent: traceparent || '', }); } diff --git a/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx b/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx index 7b558f6a05d3c1..9ea5856434c520 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx +++ b/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx @@ -25,6 +25,7 @@ export const useDeleteIndexAndTargetIndex = (items: TransformListRow[]) => { http, savedObjects, ml: { extractErrorMessage }, + application: { capabilities }, } = useAppDependencies(); const toastNotifications = useToastNotifications(); @@ -32,6 +33,7 @@ export const useDeleteIndexAndTargetIndex = (items: TransformListRow[]) => { const [deleteIndexPattern, setDeleteIndexPattern] = useState(true); const [userCanDeleteIndex, setUserCanDeleteIndex] = useState(false); const [indexPatternExists, setIndexPatternExists] = useState(false); + const [userCanDeleteDataView, setUserCanDeleteDataView] = useState(false); const toggleDeleteIndex = useCallback( () => setDeleteDestIndex(!deleteDestIndex), @@ -70,6 +72,13 @@ export const useDeleteIndexAndTargetIndex = (items: TransformListRow[]) => { if (userCanDelete) { setUserCanDeleteIndex(true); } + const canDeleteDataView = + capabilities.savedObjectsManagement.delete === true || + capabilities.indexPatterns.save === true; + setUserCanDeleteDataView(canDeleteDataView); + if (canDeleteDataView === false) { + setDeleteIndexPattern(false); + } } catch (e) { toastNotifications.addDanger( i18n.translate( @@ -80,7 +89,7 @@ export const useDeleteIndexAndTargetIndex = (items: TransformListRow[]) => { ) ); } - }, [http, toastNotifications]); + }, [http, toastNotifications, capabilities]); useEffect(() => { checkUserIndexPermission(); @@ -99,6 +108,7 @@ export const useDeleteIndexAndTargetIndex = (items: TransformListRow[]) => { return { userCanDeleteIndex, + userCanDeleteDataView, deleteDestIndex, indexPatternExists, deleteIndexPattern, diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/delete_action_modal.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/delete_action_modal.tsx index 4292f96859aafc..2986fcce554e81 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/delete_action_modal.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/delete_action_modal.tsx @@ -29,6 +29,7 @@ export const DeleteActionModal: FC = ({ toggleDeleteIndex, toggleDeleteIndexPattern, userCanDeleteIndex, + userCanDeleteDataView, }) => { const isBulkAction = items.length > 1; @@ -74,6 +75,7 @@ export const DeleteActionModal: FC = ({ )} checked={deleteIndexPattern} onChange={toggleDeleteIndexPattern} + disabled={userCanDeleteDataView === false} /> } @@ -114,6 +116,7 @@ export const DeleteActionModal: FC = ({ )} checked={deleteIndexPattern} onChange={toggleDeleteIndexPattern} + disabled={userCanDeleteDataView === false} /> )} diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/use_delete_action.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/use_delete_action.tsx index ff0ad94eba91cd..8c2fb6c96417e7 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/use_delete_action.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/use_delete_action.tsx @@ -38,6 +38,7 @@ export const useDeleteAction = (forceDisable: boolean) => { const { userCanDeleteIndex, + userCanDeleteDataView, deleteDestIndex, indexPatternExists, deleteIndexPattern, @@ -50,7 +51,7 @@ export const useDeleteAction = (forceDisable: boolean) => { const shouldDeleteDestIndex = userCanDeleteIndex && deleteDestIndex; const shouldDeleteDestIndexPattern = - userCanDeleteIndex && indexPatternExists && deleteIndexPattern; + userCanDeleteIndex && userCanDeleteDataView && indexPatternExists && deleteIndexPattern; // if we are deleting multiple transforms, then force delete all if at least one item has failed // else, force delete only when the item user picks has failed const forceDelete = isBulkAction @@ -113,5 +114,6 @@ export const useDeleteAction = (forceDisable: boolean) => { toggleDeleteIndex, toggleDeleteIndexPattern, userCanDeleteIndex, + userCanDeleteDataView, }; }; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 229a61d88e590d..b983eaaeaae47b 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -10087,7 +10087,6 @@ "xpack.fleet.agentEnrollment.agentsNotInitializedText": "エージェントを登録する前に、{link}。", "xpack.fleet.agentEnrollment.closeFlyoutButtonLabel": "閉じる", "xpack.fleet.agentEnrollment.copyPolicyButton": "クリップボードにコピー", - "xpack.fleet.agentEnrollment.copyRunInstructionsButton": "クリップボードにコピー", "xpack.fleet.agentEnrollment.downloadDescription": "FleetサーバーはElasticエージェントで実行されます。Elasticエージェントダウンロードページでは、Elasticエージェントバイナリと検証署名をダウンロードできます。", "xpack.fleet.agentEnrollment.downloadLink": "ダウンロードページに移動", "xpack.fleet.agentEnrollment.downloadPolicyButton": "ポリシーのダウンロード", @@ -10375,7 +10374,6 @@ "xpack.fleet.editPackagePolicy.upgradePageTitleWithPackageName": "{packageName}統合をアップグレード", "xpack.fleet.enrollemntAPIKeyList.emptyMessage": "登録トークンが見つかりません。", "xpack.fleet.enrollemntAPIKeyList.loadingTokensMessage": "登録トークンを読み込んでいます...", - "xpack.fleet.enrollmentInstructions.descriptionText": "エージェントのディレクトリから、該当するコマンドを実行し、Elasticエージェントをインストール、登録、起動します。これらのコマンドを再利用すると、複数のホストでエージェントを設定できます。管理者権限が必要です。", "xpack.fleet.enrollmentInstructions.moreInstructionsLink": "Elastic エージェントドキュメント", "xpack.fleet.enrollmentInstructions.moreInstructionsText": "RPM/DEB デプロイの手順については、{link}を参照してください。", "xpack.fleet.enrollmentInstructions.platformButtons.linux": "Linux / macOS", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index b17c984ac24744..8b0686d0a309f6 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10178,7 +10178,6 @@ "xpack.fleet.agentEnrollment.agentsNotInitializedText": "注册代理前,请{link}。", "xpack.fleet.agentEnrollment.closeFlyoutButtonLabel": "关闭", "xpack.fleet.agentEnrollment.copyPolicyButton": "复制到剪贴板", - "xpack.fleet.agentEnrollment.copyRunInstructionsButton": "复制到剪贴板", "xpack.fleet.agentEnrollment.downloadDescription": "Fleet 服务器运行在 Elastic 代理上。可从 Elastic 的下载页面下载 Elastic 代理二进制文件及验证签名。", "xpack.fleet.agentEnrollment.downloadLink": "前往下载页面", "xpack.fleet.agentEnrollment.downloadPolicyButton": "下载策略", @@ -10476,7 +10475,6 @@ "xpack.fleet.editPackagePolicy.upgradePageTitleWithPackageName": "升级 {packageName} 集成", "xpack.fleet.enrollemntAPIKeyList.emptyMessage": "未找到任何注册令牌。", "xpack.fleet.enrollemntAPIKeyList.loadingTokensMessage": "正在加载注册令牌......", - "xpack.fleet.enrollmentInstructions.descriptionText": "从代理目录运行相应命令,以安装、注册并启动 Elastic 代理。您可以重复使用这些命令在多个主机上设置代理。需要管理员权限。", "xpack.fleet.enrollmentInstructions.moreInstructionsLink": "Elastic 代理文档", "xpack.fleet.enrollmentInstructions.moreInstructionsText": "有关 RPM/DEB 部署说明,请参见 {link}。", "xpack.fleet.enrollmentInstructions.platformButtons.linux": "Linux/macOS", diff --git a/x-pack/plugins/uptime/common/constants/rest_api.ts b/x-pack/plugins/uptime/common/constants/rest_api.ts index 26b2c7aad20acb..bef84c41796d96 100644 --- a/x-pack/plugins/uptime/common/constants/rest_api.ts +++ b/x-pack/plugins/uptime/common/constants/rest_api.ts @@ -35,4 +35,7 @@ export enum API_URLS { DELETE_RULE = '/api/alerting/rule/', RULES_FIND = '/api/alerting/rules/_find', CONNECTOR_TYPES = '/api/actions/connector_types', + + // Service end points + INDEX_TEMPLATES = '/api/uptime/service/index_templates', } diff --git a/x-pack/plugins/uptime/common/runtime_types/synthetics_service_api_key.ts b/x-pack/plugins/uptime/common/runtime_types/synthetics_service_api_key.ts new file mode 100644 index 00000000000000..e216c0f791203c --- /dev/null +++ b/x-pack/plugins/uptime/common/runtime_types/synthetics_service_api_key.ts @@ -0,0 +1,26 @@ +/* + * 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 * as t from 'io-ts'; + +export const SyntheticsServiceApiKeyType = t.type({ + id: t.string, + name: t.string, + apiKey: t.string, +}); + +export const SyntheticsServiceApiKeySaveType = t.intersection([ + t.type({ + success: t.boolean, + }), + t.partial({ + error: t.string, + }), +]); + +export type SyntheticsServiceApiKey = t.TypeOf; +export type SyntheticsServiceApiKeySaveResponse = t.TypeOf; diff --git a/x-pack/plugins/uptime/kibana.json b/x-pack/plugins/uptime/kibana.json index 409436d734011c..803d1d82d933a4 100644 --- a/x-pack/plugins/uptime/kibana.json +++ b/x-pack/plugins/uptime/kibana.json @@ -6,21 +6,24 @@ "id": "uptime", "kibanaVersion": "kibana", "optionalPlugins": [ + "cloud", "data", + "fleet", "home", - "ml", - "fleet" + "ml" ], "requiredPlugins": [ "alerting", "embeddable", + "encryptedSavedObjects", "inspector", "features", "licensing", - "triggersActionsUi", - "usageCollection", + "observability", "ruleRegistry", - "observability" + "security", + "triggersActionsUi", + "usageCollection" ], "server": true, "ui": true, diff --git a/x-pack/plugins/uptime/server/kibana.index.ts b/x-pack/plugins/uptime/server/kibana.index.ts index 131510e62c5d90..945a4295148a26 100644 --- a/x-pack/plugins/uptime/server/kibana.index.ts +++ b/x-pack/plugins/uptime/server/kibana.index.ts @@ -11,7 +11,7 @@ import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/server'; import { PLUGIN } from '../common/constants/plugin'; import { compose } from './lib/compose/kibana'; import { initUptimeServer } from './uptime_server'; -import { UptimeCorePlugins, UptimeCoreSetup } from './lib/adapters/framework'; +import { UptimeCorePluginsSetup, UptimeCoreSetup } from './lib/adapters/framework'; import { umDynamicSettings } from './lib/saved_objects/uptime_settings'; import { UptimeRuleRegistry } from './plugin'; @@ -29,7 +29,7 @@ export interface KibanaServer extends Server { export const initServerWithKibana = ( server: UptimeCoreSetup, - plugins: UptimeCorePlugins, + plugins: UptimeCorePluginsSetup, ruleRegistry: UptimeRuleRegistry, logger: Logger ) => { diff --git a/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts index d9648a8aae575e..029c6164c04812 100644 --- a/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts @@ -12,12 +12,19 @@ import type { IScopedClusterClient, } from 'src/core/server'; import { ObservabilityPluginSetup } from '../../../../../observability/server'; +import { + EncryptedSavedObjectsPluginSetup, + EncryptedSavedObjectsPluginStart, +} from '../../../../../encrypted_saved_objects/server'; import { UMKibanaRoute } from '../../../rest_api'; import { PluginSetupContract } from '../../../../../features/server'; import { MlPluginSetup as MlSetup } from '../../../../../ml/server'; import { RuleRegistryPluginSetupContract } from '../../../../../rule_registry/server'; import { UptimeESClient } from '../../lib'; import type { UptimeRouter } from '../../../types'; +import { SecurityPluginStart } from '../../../../../security/server'; +import { CloudSetup } from '../../../../../cloud/server'; +import { FleetStartContract } from '../../../../../fleet/server'; import { UptimeConfig } from '../../../../common/config'; export type UMElasticsearchQueryFn = ( @@ -35,16 +42,27 @@ export type UMSavedObjectsQueryFn = ( export interface UptimeCoreSetup { router: UptimeRouter; config: UptimeConfig; + cloud?: CloudSetup; + fleet: FleetStartContract; + security: SecurityPluginStart; + encryptedSavedObjects: EncryptedSavedObjectsPluginStart; } -export interface UptimeCorePlugins { +export interface UptimeCorePluginsSetup { features: PluginSetupContract; alerting: any; - elasticsearch: any; observability: ObservabilityPluginSetup; usageCollection: UsageCollectionSetup; ml: MlSetup; + cloud?: CloudSetup; ruleRegistry: RuleRegistryPluginSetupContract; + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup; +} + +export interface UptimeCorePluginsStart { + security: SecurityPluginStart; + fleet: FleetStartContract; + encryptedSavedObjects: EncryptedSavedObjectsPluginStart; } export interface UMBackendFrameworkAdapter { diff --git a/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts b/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts index cf241386ec2771..7dc962c38fec73 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts @@ -19,7 +19,7 @@ import { DURATION_ANOMALY } from '../../../common/constants/alerts'; import { commonStateTranslations, durationAnomalyTranslations } from './translations'; import { AnomaliesTableRecord } from '../../../../ml/common/types/anomalies'; import { getSeverityType } from '../../../../ml/common/util/anomaly_utils'; -import { UptimeCorePlugins } from '../adapters/framework'; +import { UptimeCorePluginsSetup } from '../adapters/framework'; import { UptimeAlertTypeFactory } from './types'; import { Ping } from '../../../common/runtime_types/ping'; import { getMLJobId } from '../../../common/lib'; @@ -45,7 +45,7 @@ export const getAnomalySummary = (anomaly: AnomaliesTableRecord, monitorInfo: Pi }; const getAnomalies = async ( - plugins: UptimeCorePlugins, + plugins: UptimeCorePluginsSetup, savedObjectsClient: SavedObjectsClientContract, params: Record, lastCheckedAt: string diff --git a/x-pack/plugins/uptime/server/lib/alerts/test_utils/index.ts b/x-pack/plugins/uptime/server/lib/alerts/test_utils/index.ts index bc9aa76cb4a5bf..6481a1e2ebdcf0 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/test_utils/index.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/test_utils/index.ts @@ -7,7 +7,7 @@ import { Logger } from 'kibana/server'; import { UMServerLibs } from '../../lib'; -import { UptimeCorePlugins, UptimeCoreSetup } from '../../adapters'; +import { UptimeCorePluginsSetup, UptimeCoreSetup } from '../../adapters'; import type { UptimeRouter } from '../../../types'; import type { IRuleDataClient } from '../../../../../rule_registry/server'; import { ruleRegistryMocks } from '../../../../../rule_registry/server/mocks'; @@ -27,8 +27,8 @@ export const bootstrapDependencies = (customRequests?: any, customPlugins: any = const router = {} as UptimeRouter; // these server/libs parameters don't have any functionality, which is fine // because we aren't testing them here - const server: UptimeCoreSetup = { router, config: {} }; - const plugins: UptimeCorePlugins = customPlugins as any; + const server = { router, config: {} } as UptimeCoreSetup; + const plugins: UptimeCorePluginsSetup = customPlugins as any; const libs: UMServerLibs = { requests: {} } as UMServerLibs; libs.requests = { ...libs.requests, ...customRequests }; return { server, libs, plugins }; diff --git a/x-pack/plugins/uptime/server/lib/alerts/types.ts b/x-pack/plugins/uptime/server/lib/alerts/types.ts index f4ac2f354d8145..f734628e61b958 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/types.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/types.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { UptimeCorePlugins, UptimeCoreSetup } from '../adapters'; +import { UptimeCorePluginsSetup, UptimeCoreSetup } from '../adapters'; import { UMServerLibs } from '../lib'; import { AlertTypeWithExecutor } from '../../../../rule_registry/server'; import { AlertInstanceContext, AlertTypeState } from '../../../../alerting/common'; @@ -32,5 +32,5 @@ export type DefaultUptimeAlertInstance = AlertTy export type UptimeAlertTypeFactory = ( server: UptimeCoreSetup, libs: UMServerLibs, - plugins: UptimeCorePlugins + plugins: UptimeCorePluginsSetup ) => DefaultUptimeAlertInstance; diff --git a/x-pack/plugins/uptime/server/lib/saved_objects/index.ts b/x-pack/plugins/uptime/server/lib/saved_objects/index.ts new file mode 100644 index 00000000000000..ee1cfbbc55acd1 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/saved_objects/index.ts @@ -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 { savedObjectsAdapter } from './saved_objects'; diff --git a/x-pack/plugins/uptime/server/lib/saved_objects/saved_objects.ts b/x-pack/plugins/uptime/server/lib/saved_objects/saved_objects.ts index 7a53a37b804e9d..5aa6b7ea7c5a9e 100644 --- a/x-pack/plugins/uptime/server/lib/saved_objects/saved_objects.ts +++ b/x-pack/plugins/uptime/server/lib/saved_objects/saved_objects.ts @@ -9,33 +9,43 @@ import { SavedObjectsErrorHelpers, SavedObjectsServiceSetup, } from '../../../../../../src/core/server'; +import { EncryptedSavedObjectsPluginSetup } from '../../../../encrypted_saved_objects/server'; + import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../common/constants'; import { DynamicSettings } from '../../../common/runtime_types'; import { UMSavedObjectsQueryFn } from '../adapters'; import { UptimeConfig } from '../../../common/config'; import { settingsObjectId, umDynamicSettings } from './uptime_settings'; import { syntheticsMonitor } from './synthetics_monitor'; - -export interface UMSavedObjectsAdapter { - config: UptimeConfig; - getUptimeDynamicSettings: UMSavedObjectsQueryFn; - setUptimeDynamicSettings: UMSavedObjectsQueryFn; -} +import { syntheticsServiceApiKey } from './service_api_key'; export const registerUptimeSavedObjects = ( savedObjectsService: SavedObjectsServiceSetup, + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup, config: UptimeConfig ) => { savedObjectsService.registerType(umDynamicSettings); if (config?.unsafe?.service.enabled) { savedObjectsService.registerType(syntheticsMonitor); + savedObjectsService.registerType(syntheticsServiceApiKey); + + encryptedSavedObjects.registerType({ + type: syntheticsServiceApiKey.name, + attributesToEncrypt: new Set(['apiKey']), + }); } }; +export interface UMSavedObjectsAdapter { + config: UptimeConfig; + getUptimeDynamicSettings: UMSavedObjectsQueryFn; + setUptimeDynamicSettings: UMSavedObjectsQueryFn; +} + export const savedObjectsAdapter: UMSavedObjectsAdapter = { config: null, - getUptimeDynamicSettings: async (client): Promise => { + getUptimeDynamicSettings: async (client) => { try { const obj = await client.get(umDynamicSettings.name, settingsObjectId); return obj?.attributes ?? DYNAMIC_SETTINGS_DEFAULTS; @@ -50,7 +60,7 @@ export const savedObjectsAdapter: UMSavedObjectsAdapter = { throw getErr; } }, - setUptimeDynamicSettings: async (client, settings): Promise => { + setUptimeDynamicSettings: async (client, settings) => { await client.create(umDynamicSettings.name, settings, { id: settingsObjectId, overwrite: true, diff --git a/x-pack/plugins/uptime/server/lib/saved_objects/service_api_key.ts b/x-pack/plugins/uptime/server/lib/saved_objects/service_api_key.ts new file mode 100644 index 00000000000000..9a85b713564610 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/saved_objects/service_api_key.ts @@ -0,0 +1,74 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { + SavedObjectsClientContract, + SavedObjectsErrorHelpers, + SavedObjectsType, +} from '../../../../../../src/core/server'; +import { SyntheticsServiceApiKey } from '../../../common/runtime_types/synthetics_service_api_key'; +import { EncryptedSavedObjectsClient } from '../../../../encrypted_saved_objects/server'; + +export const syntheticsApiKeyID = 'ba997842-b0cf-4429-aa9d-578d9bf0d391'; +const syntheticsApiKeyObjectType = 'uptime-synthetics-api-key'; + +export const syntheticsServiceApiKey: SavedObjectsType = { + name: syntheticsApiKeyObjectType, + hidden: true, + namespaceType: 'single', + mappings: { + dynamic: false, + properties: { + apiKey: { + type: 'binary', + }, + /* Leaving these commented to make it clear that these fields exist, even though we don't want them indexed. + When adding new fields please add them here. If they need to be searchable put them in the uncommented + part of properties. + id: { + type: 'keyword', + }, + name: { + type: 'long', + }, + */ + }, + }, + management: { + importableAndExportable: false, + icon: 'uptimeApp', + getTitle: () => + i18n.translate('xpack.uptime.synthetics.service.apiKey', { + defaultMessage: 'Synthetics service api key', + }), + }, +}; + +export const getSyntheticsServiceAPIKey = async (client: EncryptedSavedObjectsClient) => { + try { + const obj = await client.getDecryptedAsInternalUser( + syntheticsServiceApiKey.name, + syntheticsApiKeyID + ); + return obj?.attributes; + } catch (getErr) { + if (SavedObjectsErrorHelpers.isNotFoundError(getErr)) { + return undefined; + } + throw getErr; + } +}; +export const setSyntheticsServiceApiKey = async ( + client: SavedObjectsClientContract, + apiKey: SyntheticsServiceApiKey +) => { + await client.create(syntheticsServiceApiKey.name, apiKey, { + id: syntheticsApiKeyID, + overwrite: true, + }); +}; diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.test.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.test.ts new file mode 100644 index 00000000000000..1d164f5dd5b625 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.test.ts @@ -0,0 +1,90 @@ +/* + * 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 { getAPIKeyForSyntheticsService } from './get_api_key'; +import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks'; +import { securityMock } from '../../../../security/server/mocks'; +import { coreMock } from '../../../../../../src/core/server/mocks'; +import { syntheticsServiceApiKey } from '../saved_objects/service_api_key'; +import { KibanaRequest } from 'kibana/server'; + +describe('getAPIKeyTest', function () { + const core = coreMock.createStart(); + const security = securityMock.createStart(); + const encryptedSavedObject = encryptedSavedObjectsMock.createStart(); + const request = {} as KibanaRequest; + + security.authc.apiKeys.areAPIKeysEnabled = jest.fn().mockReturnValue(true); + security.authc.apiKeys.create = jest.fn().mockReturnValue({ + id: 'test', + name: 'service-api-key', + api_key: 'qwerty', + encoded: '@#$%^&', + }); + + it('should generate an api key and return it', async () => { + const apiKey = await getAPIKeyForSyntheticsService({ + request, + security, + encryptedSavedObject, + savedObjectsClient: core.savedObjects.getScopedClient(request), + }); + + expect(security.authc.apiKeys.areAPIKeysEnabled).toHaveBeenCalledTimes(1); + expect(security.authc.apiKeys.create).toHaveBeenCalledTimes(1); + expect(security.authc.apiKeys.create).toHaveBeenCalledWith( + {}, + { + name: 'synthetics-api-key', + role_descriptors: { + synthetics_writer: { + cluster: ['monitor', 'read_ilm', 'read_pipeline'], + index: [ + { + names: ['synthetics-*'], + privileges: ['view_index_metadata', 'create_doc', 'auto_configure'], + }, + ], + }, + }, + metadata: { + description: + 'Created for synthetics service to be passed to the heartbeat to communicate with ES', + }, + } + ); + expect(apiKey).toEqual({ apiKey: 'qwerty', id: 'test', name: 'service-api-key' }); + }); + + it('should return existing api key', async () => { + const getObject = jest + .fn() + .mockReturnValue({ attributes: { apiKey: 'qwerty', id: 'test', name: 'service-api-key' } }); + + encryptedSavedObject.getClient = jest.fn().mockReturnValue({ + getDecryptedAsInternalUser: getObject, + }); + const apiKey = await getAPIKeyForSyntheticsService({ + request, + security, + encryptedSavedObject, + savedObjectsClient: core.savedObjects.getScopedClient(request), + }); + + expect(apiKey).toEqual({ apiKey: 'qwerty', id: 'test', name: 'service-api-key' }); + + expect(encryptedSavedObject.getClient).toHaveBeenCalledTimes(1); + expect(getObject).toHaveBeenCalledTimes(1); + expect(encryptedSavedObject.getClient).toHaveBeenCalledWith({ + includedHiddenTypes: [syntheticsServiceApiKey.name], + }); + expect(getObject).toHaveBeenCalledWith( + 'uptime-synthetics-api-key', + 'ba997842-b0cf-4429-aa9d-578d9bf0d391' + ); + }); +}); diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.ts new file mode 100644 index 00000000000000..2a291c64ca2b28 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/synthetics_service/get_api_key.ts @@ -0,0 +1,85 @@ +/* + * 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 { KibanaRequest, SavedObjectsClientContract } from '../../../../../../src/core/server'; +import { EncryptedSavedObjectsPluginStart } from '../../../../encrypted_saved_objects/server'; +import { SecurityPluginStart } from '../../../../security/server'; +import { + getSyntheticsServiceAPIKey, + setSyntheticsServiceApiKey, + syntheticsServiceApiKey, +} from '../saved_objects/service_api_key'; +import { SyntheticsServiceApiKey } from '../../../common/runtime_types/synthetics_service_api_key'; + +export const getAPIKeyForSyntheticsService = async ({ + encryptedSavedObject, + savedObjectsClient, + request, + security, +}: { + encryptedSavedObject: EncryptedSavedObjectsPluginStart; + request: KibanaRequest; + security: SecurityPluginStart; + savedObjectsClient: SavedObjectsClientContract; +}): Promise => { + const encryptedClient = encryptedSavedObject.getClient({ + includedHiddenTypes: [syntheticsServiceApiKey.name], + }); + + const apiKey = await getSyntheticsServiceAPIKey(encryptedClient); + if (apiKey) { + return apiKey; + } + return await generateAndSaveAPIKey({ request, security, savedObjectsClient }); +}; + +export const generateAndSaveAPIKey = async ({ + security, + request, + savedObjectsClient, +}: { + security: SecurityPluginStart; + request: KibanaRequest; + savedObjectsClient: SavedObjectsClientContract; +}) => { + try { + const isApiKeysEnabled = await security.authc.apiKeys?.areAPIKeysEnabled(); + + if (!isApiKeysEnabled) { + return new Error('Please enable API keys in kibana to use synthetics service.'); + } + + const apiKeyResult = await security.authc.apiKeys?.create(request, { + name: 'synthetics-api-key', + role_descriptors: { + synthetics_writer: { + cluster: ['monitor', 'read_ilm', 'read_pipeline'], + index: [ + { + names: ['synthetics-*'], + privileges: ['view_index_metadata', 'create_doc', 'auto_configure'], + }, + ], + }, + }, + metadata: { + description: + 'Created for synthetics service to be passed to the heartbeat to communicate with ES', + }, + }); + + if (apiKeyResult) { + const { id, name, api_key: apiKey } = apiKeyResult; + const apiKeyObject = { id, name, apiKey }; + // discard decoded key and rest of the keys + await setSyntheticsServiceApiKey(savedObjectsClient, apiKeyObject); + return apiKeyObject; + } + } catch (e) { + throw e; + } +}; diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/get_es_hosts.test.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/get_es_hosts.test.ts new file mode 100644 index 00000000000000..f028d5e154a567 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/synthetics_service/get_es_hosts.test.ts @@ -0,0 +1,65 @@ +/* + * 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. + */ + +/* + * 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 { getEsHosts } from './get_es_hosts'; +import { CloudSetup } from '../../../../cloud/server'; + +describe('getEsHostsTest', () => { + const cloudSetup = { + cloudId: + 'TLS_Test:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlvJDI0ZDYwY2NjYmZjODRhZmZhNGRjYTQ3M2M2YjFlZDgwJGUxMjkyY2YzMTczZTRkNTViZDViM2NlNzYyZDg1NzY3', + isCloudEnabled: true, + } as CloudSetup; + + it('should return expected host in cloud', function () { + const esHosts = getEsHosts({ + cloud: cloudSetup, + config: {}, + }); + + expect(esHosts).toEqual([ + 'https://24d60cccbfc84affa4dca473c6b1ed80.us-central1.gcp.cloud.es.io:443', + ]); + }); + + it('should return expected host from config', function () { + const esHosts = getEsHosts({ + config: { + unsafe: { + service: { + hosts: ['http://localhost:9200'], + }, + }, + }, + }); + + expect(esHosts).toEqual(['http://localhost:9200']); + }); + it('should return cloud hosts when both config and cloud are present', function () { + const esHosts = getEsHosts({ + cloud: cloudSetup, + config: { + unsafe: { + service: { + hosts: ['http://localhost:9200'], + }, + }, + }, + }); + + expect(esHosts).toEqual([ + 'https://24d60cccbfc84affa4dca473c6b1ed80.us-central1.gcp.cloud.es.io:443', + ]); + }); +}); diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/get_es_hosts.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/get_es_hosts.ts new file mode 100644 index 00000000000000..d0de73b73e23e5 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/synthetics_service/get_es_hosts.ts @@ -0,0 +1,40 @@ +/* + * 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. + */ + +/* + * 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 { CloudSetup } from '../../../../cloud/server'; +import { decodeCloudId } from '../../../../fleet/common'; +import { UptimeConfig } from '../../../common/config'; + +export function getEsHosts({ + cloud, + config, +}: { + cloud?: CloudSetup; + config: UptimeConfig; +}): string[] { + const cloudId = cloud?.isCloudEnabled && cloud.cloudId; + const cloudUrl = cloudId && decodeCloudId(cloudId)?.elasticsearchUrl; + const cloudHosts = cloudUrl ? [cloudUrl] : undefined; + if (cloudHosts && cloudHosts.length > 0) { + return cloudHosts; + } + + const flagHosts = config?.unsafe?.service?.hosts; + + if (flagHosts && flagHosts.length > 0) { + return flagHosts; + } + + return []; +} diff --git a/x-pack/plugins/uptime/server/plugin.ts b/x-pack/plugins/uptime/server/plugin.ts index b1b85eb943c813..42764972571112 100644 --- a/x-pack/plugins/uptime/server/plugin.ts +++ b/x-pack/plugins/uptime/server/plugin.ts @@ -12,27 +12,36 @@ import { Plugin as PluginType, ISavedObjectsRepository, Logger, + SavedObjectsClient, } from '../../../../src/core/server'; import { uptimeRuleFieldMap } from '../common/rules/uptime_rule_field_map'; import { initServerWithKibana } from './kibana.index'; -import { KibanaTelemetryAdapter, UptimeCorePlugins } from './lib/adapters'; +import { + KibanaTelemetryAdapter, + UptimeCorePluginsSetup, + UptimeCorePluginsStart, + UptimeCoreSetup, +} from './lib/adapters'; import { registerUptimeSavedObjects, savedObjectsAdapter } from './lib/saved_objects/saved_objects'; import { mappingFromFieldMap } from '../../rule_registry/common/mapping_from_field_map'; import { Dataset } from '../../rule_registry/server'; import { UptimeConfig } from '../common/config'; +import { installSyntheticsIndexTemplates } from './rest_api/synthetics_service/install_index_templates'; export type UptimeRuleRegistry = ReturnType['ruleRegistry']; export class Plugin implements PluginType { private savedObjectsClient?: ISavedObjectsRepository; private initContext: PluginInitializerContext; - private logger?: Logger; + private logger: Logger; + private server?: UptimeCoreSetup; - constructor(_initializerContext: PluginInitializerContext) { - this.initContext = _initializerContext; + constructor(initializerContext: PluginInitializerContext) { + this.initContext = initializerContext; + this.logger = initializerContext.logger.get(); } - public setup(core: CoreSetup, plugins: UptimeCorePlugins) { + public setup(core: CoreSetup, plugins: UptimeCorePluginsSetup) { const config = this.initContext.config.get(); savedObjectsAdapter.config = config; @@ -53,14 +62,15 @@ export class Plugin implements PluginType { ], }); - initServerWithKibana( - { router: core.http.createRouter(), config }, - plugins, - ruleDataClient, - this.logger - ); + this.server = { + config, + router: core.http.createRouter(), + cloud: plugins.cloud, + } as UptimeCoreSetup; + + initServerWithKibana(this.server, plugins, ruleDataClient, this.logger); - registerUptimeSavedObjects(core.savedObjects, config); + registerUptimeSavedObjects(core.savedObjects, plugins.encryptedSavedObjects, config); KibanaTelemetryAdapter.registerUsageCollector( plugins.usageCollection, @@ -72,8 +82,33 @@ export class Plugin implements PluginType { }; } - public start(core: CoreStart, _plugins: any) { + public start(core: CoreStart, plugins: UptimeCorePluginsStart) { this.savedObjectsClient = core.savedObjects.createInternalRepository(); + if (this.server) { + this.server.security = plugins.security; + this.server.fleet = plugins.fleet; + this.server.encryptedSavedObjects = plugins.encryptedSavedObjects; + } + + if (this.server?.config?.unsafe?.service.enabled) { + const esClient = core.elasticsearch.client.asInternalUser; + installSyntheticsIndexTemplates({ + esClient, + server: this.server, + savedObjectsClient: new SavedObjectsClient(core.savedObjects.createInternalRepository()), + }).then( + (result) => { + if (result.name === 'synthetics' && result.install_status === 'installed') { + this.logger.info('Installed synthetics index templates'); + } else if (result.name === 'synthetics' && result.install_status === 'install_failed') { + this.logger.warn('Failed to install synthetics index templates'); + } + }, + () => { + this.logger.warn('Failed to install synthetics index templates'); + } + ); + } } public stop() {} diff --git a/x-pack/plugins/uptime/server/rest_api/create_route_with_auth.ts b/x-pack/plugins/uptime/server/rest_api/create_route_with_auth.ts index 8b6add27f889a7..c3d7c693ef00a5 100644 --- a/x-pack/plugins/uptime/server/rest_api/create_route_with_auth.ts +++ b/x-pack/plugins/uptime/server/rest_api/create_route_with_auth.ts @@ -20,6 +20,7 @@ export const createRouteWithAuth = ( request, response, savedObjectsClient, + server, }) => { const { statusCode, message } = libs.license(context.licensing.license); if (statusCode === 200) { @@ -29,6 +30,7 @@ export const createRouteWithAuth = ( request, response, savedObjectsClient, + server, }); } switch (statusCode) { diff --git a/x-pack/plugins/uptime/server/rest_api/index.ts b/x-pack/plugins/uptime/server/rest_api/index.ts index d5aadf079931d3..344dd4d203d8d5 100644 --- a/x-pack/plugins/uptime/server/rest_api/index.ts +++ b/x-pack/plugins/uptime/server/rest_api/index.ts @@ -27,6 +27,7 @@ import { createGetIndexStatusRoute } from './index_state'; import { createNetworkEventsRoute } from './network_events'; import { createJourneyFailedStepsRoute } from './pings/journeys'; import { createLastSuccessfulStepRoute } from './synthetics/last_successful_step'; +import { installIndexTemplatesRoute } from './synthetics_service/install_index_templates'; export * from './types'; export { createRouteWithAuth } from './create_route_with_auth'; @@ -51,4 +52,5 @@ export const restApiRoutes: UMRestApiRouteFactory[] = [ createJourneyFailedStepsRoute, createLastSuccessfulStepRoute, createJourneyScreenshotBlocksRoute, + installIndexTemplatesRoute, ]; diff --git a/x-pack/plugins/uptime/server/rest_api/synthetics_service/install_index_templates.ts b/x-pack/plugins/uptime/server/rest_api/synthetics_service/install_index_templates.ts new file mode 100644 index 00000000000000..b40c6018f966bb --- /dev/null +++ b/x-pack/plugins/uptime/server/rest_api/synthetics_service/install_index_templates.ts @@ -0,0 +1,43 @@ +/* + * 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 { ElasticsearchClient, SavedObjectsClientContract } from 'kibana/server'; +import { UMRestApiRouteFactory } from '../types'; +import { API_URLS } from '../../../common/constants'; +import { UptimeCoreSetup } from '../../lib/adapters'; + +export const installIndexTemplatesRoute: UMRestApiRouteFactory = () => ({ + method: 'GET', + path: API_URLS.INDEX_TEMPLATES, + validate: {}, + handler: async ({ server, request, savedObjectsClient, uptimeEsClient }): Promise => { + return installSyntheticsIndexTemplates({ + server, + savedObjectsClient, + esClient: uptimeEsClient.baseESClient, + }); + }, +}); + +export async function installSyntheticsIndexTemplates({ + esClient, + server, + savedObjectsClient, +}: { + server: UptimeCoreSetup; + esClient: ElasticsearchClient; + savedObjectsClient: SavedObjectsClientContract; +}) { + // no need to add error handling here since fleetSetupCompleted is already wrapped in try/catch and will log + // warning if setup fails to complete + await server.fleet.fleetSetupCompleted(); + + return await server.fleet.packageService.ensureInstalledPackage({ + esClient, + savedObjectsClient, + pkgName: 'synthetics', + }); +} diff --git a/x-pack/plugins/uptime/server/rest_api/types.ts b/x-pack/plugins/uptime/server/rest_api/types.ts index ea083fc04174e9..f8027cefd3f589 100644 --- a/x-pack/plugins/uptime/server/rest_api/types.ts +++ b/x-pack/plugins/uptime/server/rest_api/types.ts @@ -17,6 +17,7 @@ import { } from 'kibana/server'; import { UMServerLibs, UptimeESClient } from '../lib/lib'; import type { UptimeRequestHandlerContext } from '../types'; +import { UptimeCoreSetup } from '../lib/adapters'; /** * Defines the basic properties employed by Uptime routes. @@ -58,7 +59,10 @@ export type UMRestApiRouteFactory = (libs: UMServerLibs) => UptimeRoute; * Functions of this type accept our internal route format and output a route * object that the Kibana platform can consume. */ -export type UMKibanaRouteWrapper = (uptimeRoute: UptimeRoute) => UMKibanaRoute; +export type UMKibanaRouteWrapper = ( + uptimeRoute: UptimeRoute, + server: UptimeCoreSetup +) => UMKibanaRoute; /** * This is the contract we specify internally for route handling. @@ -68,6 +72,7 @@ export type UMRouteHandler = ({ context, request, response, + server, savedObjectsClient, }: { uptimeEsClient: UptimeESClient; @@ -75,4 +80,5 @@ export type UMRouteHandler = ({ request: KibanaRequest, Record, Record>; response: KibanaResponseFactory; savedObjectsClient: SavedObjectsClientContract; + server: UptimeCoreSetup; }) => IKibanaResponse | Promise>; diff --git a/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts b/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts index ddde993cc9c705..cd25e0e7426250 100644 --- a/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts +++ b/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts @@ -12,7 +12,7 @@ import { createUptimeESClient, inspectableEsQueriesMap } from '../lib/lib'; import { KibanaResponse } from '../../../../../src/core/server/http/router'; import { enableInspectEsQueries } from '../../../observability/common'; -export const uptimeRouteWrapper: UMKibanaRouteWrapper = (uptimeRoute) => ({ +export const uptimeRouteWrapper: UMKibanaRouteWrapper = (uptimeRoute, server) => ({ ...uptimeRoute, options: { tags: ['access:uptime-read', ...(uptimeRoute?.writeAccess ? ['access:uptime-write'] : [])], @@ -40,6 +40,7 @@ export const uptimeRouteWrapper: UMKibanaRouteWrapper = (uptimeRoute) => ({ context, request, response, + server, }); if (res instanceof KibanaResponse) { diff --git a/x-pack/plugins/uptime/server/uptime_server.ts b/x-pack/plugins/uptime/server/uptime_server.ts index ded76027a3c3a6..ae606d7d4c3bf2 100644 --- a/x-pack/plugins/uptime/server/uptime_server.ts +++ b/x-pack/plugins/uptime/server/uptime_server.ts @@ -9,7 +9,7 @@ import { Logger } from 'kibana/server'; import { createLifecycleRuleTypeFactory, IRuleDataClient } from '../../rule_registry/server'; import { UMServerLibs } from './lib/lib'; import { createRouteWithAuth, restApiRoutes, uptimeRouteWrapper } from './rest_api'; -import { UptimeCoreSetup, UptimeCorePlugins } from './lib/adapters'; +import { UptimeCoreSetup, UptimeCorePluginsSetup } from './lib/adapters'; import { statusCheckAlertFactory } from './lib/alerts/status_check'; import { tlsAlertFactory } from './lib/alerts/tls'; @@ -19,12 +19,12 @@ import { durationAnomalyAlertFactory } from './lib/alerts/duration_anomaly'; export const initUptimeServer = ( server: UptimeCoreSetup, libs: UMServerLibs, - plugins: UptimeCorePlugins, + plugins: UptimeCorePluginsSetup, ruleDataClient: IRuleDataClient, logger: Logger ) => { restApiRoutes.forEach((route) => - libs.framework.registerRoute(uptimeRouteWrapper(createRouteWithAuth(libs, route))) + libs.framework.registerRoute(uptimeRouteWrapper(createRouteWithAuth(libs, route), server)) ); const { diff --git a/x-pack/test/api_integration/apis/search/search.ts b/x-pack/test/api_integration/apis/search/search.ts index 45e8933bf715fb..d36121a102a283 100644 --- a/x-pack/test/api_integration/apis/search/search.ts +++ b/x-pack/test/api_integration/apis/search/search.ts @@ -250,7 +250,8 @@ export default function ({ getService }: FtrProviderContext) { }); }); - describe('delete', () => { + // FLAKY: https://github.com/elastic/kibana/issues/119272 + describe.skip('delete', () => { it('should return 404 when no search id provided', async () => { await supertest.delete(`/internal/search/ese`).set('kbn-xsrf', 'foo').send().expect(404); }); diff --git a/x-pack/test/functional/apps/infra/home_page.ts b/x-pack/test/functional/apps/infra/home_page.ts index dfb5ba1cba4fd2..9c53ba20d38de4 100644 --- a/x-pack/test/functional/apps/infra/home_page.ts +++ b/x-pack/test/functional/apps/infra/home_page.ts @@ -35,7 +35,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); - describe('with metrics present', () => { + // FLAKY: https://github.com/elastic/kibana/issues/119763 + describe.skip('with metrics present', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs'); await pageObjects.common.navigateToApp('infraOps'); diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts index 55a11e6ec2d20f..756b43fdad6049 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts @@ -15,7 +15,8 @@ import { sampleLogTestData, } from './index_test_data'; -export default function ({ getService }: FtrProviderContext) { +export default function ({ getPageObject, getService }: FtrProviderContext) { + const headerPage = getPageObject('header'); const esArchiver = getService('esArchiver'); const ml = getService('ml'); @@ -42,6 +43,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataVisualizerIndexBased.clickUseFullDataButton( testData.expected.totalDocCountFormatted ); + await headerPage.waitUntilLoadingHasFinished(); await ml.testExecution.logTestStep( `${testData.suiteTitle} displays elements in the doc count panel correctly` @@ -166,8 +168,7 @@ export default function ({ getService }: FtrProviderContext) { await ml.securityUI.loginAsMlPowerUser(); }); - // FLAKY: https://github.com/elastic/kibana/issues/118472 - describe.skip('with farequote', function () { + describe('with farequote', function () { // Run tests on full farequote index. it(`${farequoteDataViewTestData.suiteTitle} loads the data visualizer selector page`, async () => { // Start navigation from the base of the ML app. diff --git a/x-pack/test/functional/apps/monitoring/_get_lifecycle_methods.js b/x-pack/test/functional/apps/monitoring/_get_lifecycle_methods.js index fce6fcfff7772c..702a3339996193 100644 --- a/x-pack/test/functional/apps/monitoring/_get_lifecycle_methods.js +++ b/x-pack/test/functional/apps/monitoring/_get_lifecycle_methods.js @@ -15,7 +15,7 @@ export const getLifecycleMethods = (getService, getPageObjects) => { async setup(archive, { from, to, useSuperUser = false }) { _archive = archive; if (!useSuperUser) { - await security.testUser.setRoles(['monitoring_user', 'kibana_admin']); + await security.testUser.setRoles(['monitoring_user', 'kibana_admin', 'test_monitoring']); } const kibanaServer = getService('kibanaServer'); diff --git a/x-pack/test/functional/apps/monitoring/cluster/list.js b/x-pack/test/functional/apps/monitoring/cluster/list.js index 09361f88f56529..61b783efe3e68e 100644 --- a/x-pack/test/functional/apps/monitoring/cluster/list.js +++ b/x-pack/test/functional/apps/monitoring/cluster/list.js @@ -13,6 +13,8 @@ export default function ({ getService, getPageObjects }) { const clusterOverview = getService('monitoringClusterOverview'); const testSubjects = getService('testSubjects'); const PageObjects = getPageObjects(['monitoring', 'header', 'common']); + const alertsService = getService('monitoringAlerts'); + const browser = getService('browser'); describe('Cluster listing', () => { describe('with trial license clusters', () => { @@ -150,5 +152,29 @@ export default function ({ getService, getPageObjects }) { }); }); }); + + describe('Alerts', () => { + const { setup, tearDown } = getLifecycleMethods(getService, getPageObjects); + + before(async () => { + await setup('x-pack/test/functional/es_archives/monitoring/multicluster', { + from: 'Aug 15, 2017 @ 21:00:00.000', + to: 'Aug 16, 2017 @ 00:00:00.000', + }); + }); + + after(async () => { + await tearDown(); + + await alertsService.deleteAlerts(); + + await browser.clearLocalStorage(); + }); + + it('should show a toast when alerts are created successfully', async () => { + await clusterList.acceptAlertsModal(); + expect(await testSubjects.exists('alertsCreatedToast', { timeout: 10000 })).to.be(true); + }); + }); }); } diff --git a/x-pack/test/functional/apps/monitoring/cluster/overview.js b/x-pack/test/functional/apps/monitoring/cluster/overview.js index 902c82f0881527..25e52535a39b28 100644 --- a/x-pack/test/functional/apps/monitoring/cluster/overview.js +++ b/x-pack/test/functional/apps/monitoring/cluster/overview.js @@ -10,6 +10,11 @@ import { getLifecycleMethods } from '../_get_lifecycle_methods'; export default function ({ getService, getPageObjects }) { const overview = getService('monitoringClusterOverview'); + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['monitoring', 'common', 'timePicker']); + const alertsService = getService('monitoringAlerts'); + const browser = getService('browser'); + const setupMode = getService('monitoringSetupMode'); describe('Cluster overview', () => { describe('for Green cluster with Gold license', () => { @@ -159,5 +164,41 @@ export default function ({ getService, getPageObjects }) { expect(await overview.doesLsPanelExist()).to.be(false); }); }); + + describe('Alerts', () => { + const { setup, tearDown } = getLifecycleMethods(getService, getPageObjects); + + before(async () => { + await setup('x-pack/test/functional/es_archives/monitoring/singlecluster_green_gold', { + from: 'Aug 23, 2017 @ 21:29:35.267', + to: 'Aug 23, 2017 @ 21:47:25.556', + }); + }); + + after(async () => { + await tearDown(); + await alertsService.deleteAlerts(); + await browser.clearLocalStorage(); + }); + + describe('when create alerts options is selected in the alerts modal', () => { + before(async () => { + await overview.acceptAlertsModal(); + }); + + it('should show a toast when alerts are created successfully', async () => { + expect(await testSubjects.exists('alertsCreatedToast', { timeout: 10000 })).to.be(true); + }); + + it('should show badges when entering setup mode', async () => { + await setupMode.clickSetupModeBtn(); + await PageObjects.timePicker.startAutoRefresh(1); + + expect(await testSubjects.exists('alertsBadge')).to.be(true); + await PageObjects.timePicker.pauseAutoRefresh(); + await setupMode.clickExitSetupModeBtn(); + }); + }); + }); }); } diff --git a/x-pack/test/functional/apps/uptime/synthetics_integration.ts b/x-pack/test/functional/apps/uptime/synthetics_integration.ts index 9346867a0f1db0..83fc3c4079619f 100644 --- a/x-pack/test/functional/apps/uptime/synthetics_integration.ts +++ b/x-pack/test/functional/apps/uptime/synthetics_integration.ts @@ -170,7 +170,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); - describe('create new policy', () => { + // FLAKY: https://github.com/elastic/kibana/issues/103390 + describe.skip('create new policy', () => { let version: string; beforeEach(async () => { diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js index 2abd91fd0433a8..de0c5dbd3699f6 100644 --- a/x-pack/test/functional/config.js +++ b/x-pack/test/functional/config.js @@ -213,6 +213,11 @@ export default async function ({ readConfigFile }) { }, security: { roles: { + test_monitoring: { + elasticsearch: { + cluster: ['monitor'], + }, + }, test_logstash_reader: { elasticsearch: { cluster: [], diff --git a/x-pack/test/functional/services/index.ts b/x-pack/test/functional/services/index.ts index 2f9259c16d4bfe..10c68456e1262f 100644 --- a/x-pack/test/functional/services/index.ts +++ b/x-pack/test/functional/services/index.ts @@ -37,6 +37,7 @@ import { MonitoringKibanaInstanceProvider, MonitoringKibanaSummaryStatusProvider, MonitoringSetupModeProvider, + MonitoringAlertsProvider, // @ts-ignore not ts yet } from './monitoring'; // @ts-ignore not ts yet @@ -101,6 +102,7 @@ export const services = { monitoringKibanaInstance: MonitoringKibanaInstanceProvider, monitoringKibanaSummaryStatus: MonitoringKibanaSummaryStatusProvider, monitoringSetupMode: MonitoringSetupModeProvider, + monitoringAlerts: MonitoringAlertsProvider, pipelineList: PipelineListProvider, pipelineEditor: PipelineEditorProvider, random: RandomProvider, diff --git a/x-pack/test/functional/services/monitoring/alerts.js b/x-pack/test/functional/services/monitoring/alerts.js new file mode 100644 index 00000000000000..c480cc0c45c033 --- /dev/null +++ b/x-pack/test/functional/services/monitoring/alerts.js @@ -0,0 +1,23 @@ +/* + * 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 function MonitoringAlertsProvider({ getService }) { + const supertest = getService('supertest'); + + return new (class MonitoringAlerts { + async deleteAlerts() { + const apiResponse = await supertest.get('/api/alerts/_find?per_page=20'); + const alerts = apiResponse.body.data.filter(({ consumer }) => consumer === 'monitoring'); + + return await Promise.all( + alerts.map(async (alert) => + supertest.delete(`/api/alerts/alert/${alert.id}`).set('kbn-xsrf', 'true').expect(204) + ) + ); + } + })(); +} diff --git a/x-pack/test/functional/services/monitoring/cluster_list.js b/x-pack/test/functional/services/monitoring/cluster_list.js index bcf0e18ef4dd7c..1873d191bca56c 100644 --- a/x-pack/test/functional/services/monitoring/cluster_list.js +++ b/x-pack/test/functional/services/monitoring/cluster_list.js @@ -46,6 +46,10 @@ export function MonitoringClusterListProvider({ getService, getPageObjects }) { return testSubjects.click(ALERTS_MODAL_BUTTON); } + acceptAlertsModal() { + return testSubjects.click('alerts-modal-button'); + } + getClusterLink(clusterUuid) { return testSubjects.find(`${SUBJ_CLUSTER_ROW_PREFIX}${clusterUuid} > clusterLink`); } diff --git a/x-pack/test/functional/services/monitoring/index.js b/x-pack/test/functional/services/monitoring/index.js index 5d337dc6ca8228..e02280c52d2c03 100644 --- a/x-pack/test/functional/services/monitoring/index.js +++ b/x-pack/test/functional/services/monitoring/index.js @@ -30,3 +30,4 @@ export { MonitoringKibanaInstancesProvider } from './kibana_instances'; export { MonitoringKibanaInstanceProvider } from './kibana_instance'; export { MonitoringKibanaSummaryStatusProvider } from './kibana_summary_status'; export { MonitoringSetupModeProvider } from './setup_mode'; +export { MonitoringAlertsProvider } from './alerts'; diff --git a/x-pack/test/functional/services/monitoring/setup_mode.js b/x-pack/test/functional/services/monitoring/setup_mode.js index 976b7b4214937d..64139fbc5016f0 100644 --- a/x-pack/test/functional/services/monitoring/setup_mode.js +++ b/x-pack/test/functional/services/monitoring/setup_mode.js @@ -13,6 +13,7 @@ export function MonitoringSetupModeProvider({ getService }) { const SUBJ_SETUP_MODE_METRICBEAT_MIGRATION_TOOLTIP = 'monitoringSetupModeMetricbeatMigrationTooltip'; const SUBJ_SETUP_MODE_ALERTS_BADGE = 'monitoringSetupModeAlertBadges'; + const SUBJ_EXIT_SETUP_MODE_BTN = 'exitSetupModeBtn'; return new (class SetupMode { async doesSetupModeBtnAppear() { @@ -34,5 +35,9 @@ export function MonitoringSetupModeProvider({ getService }) { async doesAlertsTooltipAppear() { return await testSubjects.exists(SUBJ_SETUP_MODE_ALERTS_BADGE); } + + async clickExitSetupModeBtn() { + return await testSubjects.click(SUBJ_EXIT_SETUP_MODE_BTN); + } })(); } diff --git a/yarn.lock b/yarn.lock index 3d1ab34b02f85b..44185306ca38f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12407,10 +12407,10 @@ elastic-apm-http-client@^10.3.0: readable-stream "^3.4.0" stream-chopper "^3.0.1" -elastic-apm-node@^3.24.0: - version "3.24.0" - resolved "https://registry.yarnpkg.com/elastic-apm-node/-/elastic-apm-node-3.24.0.tgz#d7acb3352f928a23c28ebabab2bd30098562814e" - integrity sha512-Fmj/W2chWQa2zb1FfMYK2ypLB4TcnKNX+1klaJFbytRYDLgeSfo0EC7egvI3a+bLPZSRL5053PXOp7slVTPO6Q== +elastic-apm-node@^3.25.0: + version "3.25.0" + resolved "https://registry.yarnpkg.com/elastic-apm-node/-/elastic-apm-node-3.25.0.tgz#3207c936429739cd07f64cbf76d7b5b4b8e0da3e" + integrity sha512-3K+uUQkKeaJarjPb/pDY3fldP7QeppgPPx8nJOkOrW+BvQK5YBMiWbf4S9fdx0yUUkWsVX6K+CAc401+Y1COkg== dependencies: "@elastic/ecs-pino-format" "^1.2.0" after-all-results "^2.0.0"