Skip to content

Commit

Permalink
[serverless] Create the Serverless Plugin (#155582)
Browse files Browse the repository at this point in the history
> Derived from #153274 for
production.

## Summary

This PR creates the `serverless` plugin for Kibana Serverless projects.


![image](https://user-images.githubusercontent.com/297604/233892935-b3713575-a2f7-4e82-a9dd-e8c11823683f.png)


It uses the methodology proven out in the proof-of-concept
(#153274) and prepares it for
production:

- Adds chrome style and related API to the `chrome` services.
- Creates the `serverless` plugin.
- Invokes the new chrome style API for all serverless projects.
- Alters `yarn` scripts to support all project types, and switching
between them.
- Creates the new "Project Switcher" component for use in the new chrome
header for Serverless.
- Creates a Storybook config for this and future components.
- Adds API endpoint to trigger project switching and `Watcher` restarts.

<img width="1598" alt="Screenshot 2023-04-26 at 10 44 01 AM"
src="https://user-images.githubusercontent.com/297604/234612654-fdcf38ea-8c48-4066-bc85-507f40c984aa.png">


## Next steps

- [x] Creating a PR for enabling/disabling related plugins for
Serverless. (#155583)
- [ ] Creating product plugin PR based on
#153274.

---------

Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
clintandrewhall and kibanamachine authored Apr 26, 2023
1 parent 5e713fb commit 8e37b38
Show file tree
Hide file tree
Showing 83 changed files with 1,621 additions and 56 deletions.
1 change: 1 addition & 0 deletions .buildkite/scripts/steps/storybooks/build_and_upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const STORYBOOKS = [
'observability',
'presentation',
'security_solution',
'serverless',
'shared_ux',
'triggers_actions_ui',
'ui_actions_enhanced',
Expand Down
4 changes: 4 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,10 @@ packages/kbn-securitysolution-t-grid @elastic/security-solution-platform
packages/kbn-securitysolution-utils @elastic/security-solution-platform
packages/kbn-server-http-tools @elastic/kibana-core
packages/kbn-server-route-repository @elastic/apm-ui
x-pack/plugins/serverless @elastic/appex-sharedux
packages/serverless/project_switcher @elastic/appex-sharedux
packages/serverless/storybook/config @elastic/appex-sharedux
packages/serverless/types @elastic/appex-sharedux
test/plugin_functional/plugins/session_notifications @elastic/kibana-core
x-pack/plugins/session_view @elastic/sec-cloudnative-integrations
packages/kbn-set-map @elastic/kibana-operations
Expand Down
1 change: 1 addition & 0 deletions .i18nrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
"share": "src/plugins/share",
"sharedUXPackages": "packages/shared-ux",
"securitySolutionPackages": "x-pack/packages/security-solution",
"serverlessPackages": "packages/serverless",
"coloring": "packages/kbn-coloring/src",
"languageDocumentationPopover": "packages/kbn-language-documentation-popover/src",
"statusPage": "src/legacy/core_plugins/status_page",
Expand Down
1 change: 1 addition & 0 deletions config/serverless.es.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
xpack.serverless.plugin.developer.projectSwitcher.currentType: 'search'
1 change: 1 addition & 0 deletions config/serverless.oblt.yml
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
xpack.infra.logs.app_target: discover
xpack.serverless.plugin.developer.projectSwitcher.currentType: 'observability'
1 change: 1 addition & 0 deletions config/serverless.security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
xpack.serverless.plugin.developer.projectSwitcher.currentType: 'security'
2 changes: 2 additions & 0 deletions config/serverless.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
newsfeed.enabled: false
xpack.security.showNavLinks: false
xpack.serverless.plugin.enabled: true
xpack.fleet.enableExperimental: ['fleetServerStandalone']
xpack.fleet.internal.disableILMPolicies: true

Expand Down
4 changes: 4 additions & 0 deletions docs/developer/plugin-list.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,10 @@ Kibana.
|Welcome to the Kibana Security Solution plugin! This README will go over getting started with development and testing.
|{kib-repo}blob/{branch}/x-pack/plugins/serverless/README.mdx[serverless]
|
|{kib-repo}blob/{branch}/x-pack/plugins/session_view/README.md[sessionView]
|Session View is meant to provide a visualization into what is going on in a particular Linux environment where the agent is running. It looks likes a terminal emulator; however, it is a tool for introspecting process activity and understanding user and service behaviour in your Linux servers and infrastructure. It is a time-ordered series of process executions displayed in a tree over time.
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"lint:es": "node scripts/eslint",
"lint:style": "node scripts/stylelint",
"makelogs": "node scripts/makelogs",
"serverless": "node scripts/kibana --dev --serverless",
"serverless-es": "node scripts/kibana --dev --serverless=es",
"serverless-oblt": "node scripts/kibana --dev --serverless=oblt",
"serverless-security": "node scripts/kibana --dev --serverless=security",
Expand Down Expand Up @@ -573,6 +574,9 @@
"@kbn/securitysolution-utils": "link:packages/kbn-securitysolution-utils",
"@kbn/server-http-tools": "link:packages/kbn-server-http-tools",
"@kbn/server-route-repository": "link:packages/kbn-server-route-repository",
"@kbn/serverless": "link:x-pack/plugins/serverless",
"@kbn/serverless-project-switcher": "link:packages/serverless/project_switcher",
"@kbn/serverless-types": "link:packages/serverless/types",
"@kbn/session-notifications-plugin": "link:test/plugin_functional/plugins/session_notifications",
"@kbn/session-view-plugin": "link:x-pack/plugins/session_view",
"@kbn/set-map": "link:packages/kbn-set-map",
Expand Down Expand Up @@ -1112,6 +1116,7 @@
"@kbn/repo-source-classifier": "link:packages/kbn-repo-source-classifier",
"@kbn/repo-source-classifier-cli": "link:packages/kbn-repo-source-classifier-cli",
"@kbn/security-api-integration-helpers": "link:x-pack/test/security_api_integration/packages/helpers",
"@kbn/serverless-storybook-config": "link:packages/serverless/storybook/config",
"@kbn/some-dev-log": "link:packages/kbn-some-dev-log",
"@kbn/sort-package-json": "link:packages/kbn-sort-package-json",
"@kbn/spec-to-console": "link:packages/kbn-spec-to-console",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ describe('start', () => {
Array [
Array [
"kbnBody",
"kbnBody--classicLayout",
"kbnBody--noHeaderBanner",
"kbnBody--chromeHidden",
"kbnVersion-1-2-3",
Expand All @@ -143,6 +144,7 @@ describe('start', () => {
Array [
Array [
"kbnBody",
"kbnBody--classicLayout",
"kbnBody--noHeaderBanner",
"kbnBody--chromeHidden",
"kbnVersion-8-0-0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ import type {
ChromeGlobalHelpExtensionMenuLink,
ChromeHelpExtension,
ChromeUserBanner,
ChromeStyle,
} from '@kbn/core-chrome-browser';
import type { CustomBrandingStart } from '@kbn/core-custom-branding-browser';
import { KIBANA_ASK_ELASTIC_LINK } from './constants';
import { DocTitleService } from './doc_title';
import { NavControlsService } from './nav_controls';
import { NavLinksService } from './nav_links';
import { RecentlyAccessedService } from './recently_accessed';
import { Header } from './ui';
import { Header, ProjectHeader } from './ui';
import type { InternalChromeStart } from './types';

const IS_LOCKED_KEY = 'core.chrome.isLocked';
Expand Down Expand Up @@ -119,6 +120,7 @@ export class ChromeService {
const customNavLink$ = new BehaviorSubject<ChromeNavLink | undefined>(undefined);
const helpSupportUrl$ = new BehaviorSubject<string>(KIBANA_ASK_ELASTIC_LINK);
const isNavDrawerLocked$ = new BehaviorSubject(localStorage.getItem(IS_LOCKED_KEY) === 'true');
const chromeStyle$ = new BehaviorSubject<ChromeStyle>('classic');

const getKbnVersionClass = () => {
// we assume that the version is valid and has the form 'X.X.X'
Expand All @@ -131,10 +133,11 @@ export class ChromeService {
};

const headerBanner$ = new BehaviorSubject<ChromeUserBanner | undefined>(undefined);
const bodyClasses$ = combineLatest([headerBanner$, this.isVisible$!]).pipe(
map(([headerBanner, isVisible]) => {
const bodyClasses$ = combineLatest([headerBanner$, this.isVisible$!, chromeStyle$]).pipe(
map(([headerBanner, isVisible, chromeStyle]) => {
return [
'kbnBody',
chromeStyle === 'project' ? 'kbnBody--projectLayout' : 'kbnBody--classicLayout',
headerBanner ? 'kbnBody--hasHeaderBanner' : 'kbnBody--noHeaderBanner',
isVisible ? 'kbnBody--chromeVisible' : 'kbnBody--chromeHidden',
getKbnVersionClass(),
Expand Down Expand Up @@ -163,6 +166,10 @@ export class ChromeService {

const getIsNavDrawerLocked$ = isNavDrawerLocked$.pipe(takeUntil(this.stop$));

const setChromeStyle = (style: ChromeStyle) => {
chromeStyle$.next(style);
};

const isIE = () => {
const ua = window.navigator.userAgent;
const msie = ua.indexOf('MSIE '); // IE 10 or older
Expand Down Expand Up @@ -203,41 +210,65 @@ export class ChromeService {
});
}

const getHeaderComponent = () => {
const Component = ({ style$ }: { style$: typeof chromeStyle$ }) => {
if (style$.getValue() === 'project') {
return (
<ProjectHeader
{...{
application,
globalHelpExtensionMenuLinks$,
}}
actionMenu$={application.currentActionMenu$}
breadcrumbs$={breadcrumbs$.pipe(takeUntil(this.stop$))}
helpExtension$={helpExtension$.pipe(takeUntil(this.stop$))}
helpSupportUrl$={helpSupportUrl$.pipe(takeUntil(this.stop$))}
navControlsRight$={navControls.getRight$()}
kibanaDocLink={docLinks.links.kibana.guide}
kibanaVersion={injectedMetadata.getKibanaVersion()}
/>
);
}

return (
<Header
loadingCount$={http.getLoadingCount$()}
application={application}
headerBanner$={headerBanner$.pipe(takeUntil(this.stop$))}
badge$={badge$.pipe(takeUntil(this.stop$))}
basePath={http.basePath}
breadcrumbs$={breadcrumbs$.pipe(takeUntil(this.stop$))}
breadcrumbsAppendExtension$={breadcrumbsAppendExtension$.pipe(takeUntil(this.stop$))}
customNavLink$={customNavLink$.pipe(takeUntil(this.stop$))}
kibanaDocLink={docLinks.links.kibana.guide}
forceAppSwitcherNavigation$={navLinks.getForceAppSwitcherNavigation$()}
globalHelpExtensionMenuLinks$={globalHelpExtensionMenuLinks$}
helpExtension$={helpExtension$.pipe(takeUntil(this.stop$))}
helpSupportUrl$={helpSupportUrl$.pipe(takeUntil(this.stop$))}
homeHref={http.basePath.prepend('/app/home')}
isVisible$={this.isVisible$}
kibanaVersion={injectedMetadata.getKibanaVersion()}
navLinks$={navLinks.getNavLinks$()}
recentlyAccessed$={recentlyAccessed.get$()}
navControlsLeft$={navControls.getLeft$()}
navControlsCenter$={navControls.getCenter$()}
navControlsRight$={navControls.getRight$()}
navControlsExtension$={navControls.getExtension$()}
onIsLockedUpdate={setIsNavDrawerLocked}
isLocked$={getIsNavDrawerLocked$}
customBranding$={customBranding$}
/>
);
};
return <Component {...{ style$: chromeStyle$ }} />;
};

return {
navControls,
navLinks,
recentlyAccessed,
docTitle,

getHeaderComponent: () => (
<Header
loadingCount$={http.getLoadingCount$()}
application={application}
headerBanner$={headerBanner$.pipe(takeUntil(this.stop$))}
badge$={badge$.pipe(takeUntil(this.stop$))}
basePath={http.basePath}
breadcrumbs$={breadcrumbs$.pipe(takeUntil(this.stop$))}
breadcrumbsAppendExtension$={breadcrumbsAppendExtension$.pipe(takeUntil(this.stop$))}
customNavLink$={customNavLink$.pipe(takeUntil(this.stop$))}
kibanaDocLink={docLinks.links.kibana.guide}
forceAppSwitcherNavigation$={navLinks.getForceAppSwitcherNavigation$()}
globalHelpExtensionMenuLinks$={globalHelpExtensionMenuLinks$}
helpExtension$={helpExtension$.pipe(takeUntil(this.stop$))}
helpSupportUrl$={helpSupportUrl$.pipe(takeUntil(this.stop$))}
homeHref={http.basePath.prepend('/app/home')}
isVisible$={this.isVisible$}
kibanaVersion={injectedMetadata.getKibanaVersion()}
navLinks$={navLinks.getNavLinks$()}
recentlyAccessed$={recentlyAccessed.get$()}
navControlsLeft$={navControls.getLeft$()}
navControlsCenter$={navControls.getCenter$()}
navControlsRight$={navControls.getRight$()}
navControlsExtension$={navControls.getExtension$()}
onIsLockedUpdate={setIsNavDrawerLocked}
isLocked$={getIsNavDrawerLocked$}
customBranding$={customBranding$}
/>
),
getHeaderComponent,

getIsVisible$: () => this.isVisible$,

Expand Down Expand Up @@ -302,6 +333,8 @@ export class ChromeService {
},

getBodyClasses$: () => bodyClasses$.pipe(takeUntil(this.stop$)),
setChromeStyle,
getChromeStyle$: () => chromeStyle$.pipe(takeUntil(this.stop$)),
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
*/

export { Header } from './header';
export { ProjectHeader } from './project';
export { LoadingIndicator } from './loading_indicator';
export type { NavType } from './header';
Original file line number Diff line number Diff line change
@@ -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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import React from 'react';
import { Router } from 'react-router-dom';
import { EuiHeader, EuiHeaderLogo, EuiHeaderSection, EuiHeaderSectionItem } from '@elastic/eui';
import {
ChromeBreadcrumb,
ChromeGlobalHelpExtensionMenuLink,
ChromeHelpExtension,
ChromeNavControl,
} from '@kbn/core-chrome-browser/src';
import { Observable } from 'rxjs';
import { MountPoint } from '@kbn/core-mount-utils-browser';
import { InternalApplicationStart } from '@kbn/core-application-browser-internal';
import { HeaderBreadcrumbs } from '../header/header_breadcrumbs';
import { HeaderActionMenu } from '../header/header_action_menu';
import { HeaderHelpMenu } from '../header/header_help_menu';
import { HeaderNavControls } from '../header/header_nav_controls';
import { ProjectNavigation } from './navigation';

interface Props {
breadcrumbs$: Observable<ChromeBreadcrumb[]>;
actionMenu$: Observable<MountPoint | undefined>;
kibanaDocLink: string;
globalHelpExtensionMenuLinks$: Observable<ChromeGlobalHelpExtensionMenuLink[]>;
helpExtension$: Observable<ChromeHelpExtension | undefined>;
helpSupportUrl$: Observable<string>;
kibanaVersion: string;
application: InternalApplicationStart;
navControlsRight$: Observable<ChromeNavControl[]>;
}

export const ProjectHeader = ({
application,
kibanaDocLink,
kibanaVersion,
...observables
}: Props) => {
const renderLogo = () => (
<EuiHeaderLogo
iconType="logoElastic"
href="#"
onClick={(e) => e.preventDefault()}
aria-label="Go to home page"
/>
);

return (
<>
<EuiHeader position="fixed">
<EuiHeaderSection grow={false}>
<EuiHeaderSectionItem border="right">{renderLogo()}</EuiHeaderSectionItem>
<EuiHeaderSectionItem>
<HeaderBreadcrumbs breadcrumbs$={observables.breadcrumbs$} />
</EuiHeaderSectionItem>
</EuiHeaderSection>
<EuiHeaderSection side="right">
<EuiHeaderSectionItem>
<HeaderHelpMenu
globalHelpExtensionMenuLinks$={observables.globalHelpExtensionMenuLinks$}
helpExtension$={observables.helpExtension$}
helpSupportUrl$={observables.helpSupportUrl$}
kibanaDocLink={kibanaDocLink}
kibanaVersion={kibanaVersion}
navigateToUrl={application.navigateToUrl}
/>
</EuiHeaderSectionItem>
<EuiHeaderSectionItem border="left">
<HeaderActionMenu actionMenu$={observables.actionMenu$} />
</EuiHeaderSectionItem>

<EuiHeaderSectionItem>
<HeaderNavControls navControls$={observables.navControlsRight$} />
</EuiHeaderSectionItem>
</EuiHeaderSection>
</EuiHeader>
<Router history={application.history}>
<ProjectNavigation>
<span />
</ProjectNavigation>
</Router>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

export { ProjectHeader } from './header';
Loading

0 comments on commit 8e37b38

Please sign in to comment.