Skip to content

Commit

Permalink
[cloud plugin] Add serverless projectId to configuration and contract (
Browse files Browse the repository at this point in the history
…#161728)

## Summary

Fix #161652

Add the `serverless.projectId` config setting to the `cloud` plugin, and
expose the `isCloudServerless` and `serverless.projectId` info from the
cloud plugin's API.
  • Loading branch information
pgayvallet authored Jul 14, 2023
1 parent e5bcc87 commit a9786df
Show file tree
Hide file tree
Showing 12 changed files with 217 additions and 4 deletions.
2 changes: 2 additions & 0 deletions test/plugin_functional/test_suites/core_plugins/rendering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
'xpack.cloud.organization_url (string)',
'xpack.cloud.billing_url (string)',
'xpack.cloud.profile_url (string)',
// can't be used to infer urls or customer id from the outside
'xpack.cloud.serverless.project_id (string)',
'xpack.discoverEnhanced.actions.exploreDataInChart.enabled (boolean)',
'xpack.discoverEnhanced.actions.exploreDataInContextMenu.enabled (boolean)',
'xpack.fleet.agents.enabled (boolean)',
Expand Down
8 changes: 8 additions & 0 deletions x-pack/plugins/cloud/public/mocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ function createSetupMock(): jest.Mocked<CloudSetup> {
isElasticStaffOwned: true,
trialEndDate: new Date('2020-10-01T14:13:12Z'),
registerCloudService: jest.fn(),
isServerlessEnabled: false,
serverless: {
projectId: undefined,
},
};
}

Expand All @@ -42,6 +46,10 @@ const createStartMock = (): jest.Mocked<CloudStart> => ({
billingUrl: 'billing-url',
profileUrl: 'profile-url',
organizationUrl: 'organization-url',
isServerlessEnabled: false,
serverless: {
projectId: undefined,
},
});

export const cloudMock = {
Expand Down
68 changes: 65 additions & 3 deletions x-pack/plugins/cloud/public/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { decodeCloudIdMock, parseDeploymentIdFromDeploymentUrlMock } from './plugin.test.mocks';
import { coreMock } from '@kbn/core/public/mocks';
import { CloudPlugin } from './plugin';
import { CloudPlugin, type CloudConfigType } from './plugin';
import type { DecodedCloudId } from '../common/decode_cloud_id';

const baseConfig = {
Expand All @@ -25,11 +25,12 @@ describe('Cloud Plugin', () => {

describe('#setup', () => {
describe('interface', () => {
const setupPlugin = () => {
const setupPlugin = (configParts: Partial<CloudConfigType> = {}) => {
const initContext = coreMock.createPluginInitializerContext({
...baseConfig,
id: 'cloudId',
cname: 'cloud.elastic.co',
...configParts,
});
const plugin = new CloudPlugin(initContext);

Expand Down Expand Up @@ -114,11 +115,38 @@ describe('Cloud Plugin', () => {
expect(decodeCloudIdMock).toHaveBeenCalledTimes(1);
expect(decodeCloudIdMock).toHaveBeenCalledWith('cloudId', expect.any(Object));
});

describe('isServerlessEnabled', () => {
it('is `true` when `serverless.projectId` is set', () => {
const { setup } = setupPlugin({
serverless: {
project_id: 'my-awesome-project',
},
});
expect(setup.isServerlessEnabled).toBe(true);
});

it('is `false` when `serverless.projectId` is not set', () => {
const { setup } = setupPlugin({
serverless: undefined,
});
expect(setup.isServerlessEnabled).toBe(false);
});
});

it('exposes `serverless.projectId`', () => {
const { setup } = setupPlugin({
serverless: {
project_id: 'my-awesome-project',
},
});
expect(setup.serverless.projectId).toBe('my-awesome-project');
});
});
});

describe('#start', () => {
const startPlugin = () => {
const startPlugin = (configParts: Partial<CloudConfigType> = {}) => {
const plugin = new CloudPlugin(
coreMock.createPluginInitializerContext({
id: 'cloudId',
Expand All @@ -132,6 +160,7 @@ describe('Cloud Plugin', () => {
chat: {
enabled: false,
},
...configParts,
})
);
const coreSetup = coreMock.createSetup();
Expand All @@ -154,5 +183,38 @@ describe('Cloud Plugin', () => {
]
`);
});

describe('isServerlessEnabled', () => {
it('is `true` when `serverless.projectId` is set', () => {
const { plugin } = startPlugin({
serverless: {
project_id: 'my-awesome-project',
},
});
const coreStart = coreMock.createStart();
const start = plugin.start(coreStart);
expect(start.isServerlessEnabled).toBe(true);
});

it('is `false` when `serverless.projectId` is not set', () => {
const { plugin } = startPlugin({
serverless: undefined,
});
const coreStart = coreMock.createStart();
const start = plugin.start(coreStart);
expect(start.isServerlessEnabled).toBe(false);
});
});

it('exposes `serverless.projectId`', () => {
const { plugin } = startPlugin({
serverless: {
project_id: 'my-awesome-project',
},
});
const coreStart = coreMock.createStart();
const start = plugin.start(coreStart);
expect(start.serverless.projectId).toBe('my-awesome-project');
});
});
});
13 changes: 13 additions & 0 deletions x-pack/plugins/cloud/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ export interface CloudConfigType {
organization_url?: string;
trial_end_date?: string;
is_elastic_staff_owned?: boolean;
serverless?: {
project_id: string;
};
}

interface CloudUrls {
Expand All @@ -39,12 +42,14 @@ interface CloudUrls {
export class CloudPlugin implements Plugin<CloudSetup> {
private readonly config: CloudConfigType;
private readonly isCloudEnabled: boolean;
private readonly isServerlessEnabled: boolean;
private readonly contextProviders: FC[] = [];
private readonly logger: Logger;

constructor(private readonly initializerContext: PluginInitializerContext) {
this.config = this.initializerContext.config.get<CloudConfigType>();
this.isCloudEnabled = getIsCloudEnabled(this.config.id);
this.isServerlessEnabled = !!this.config.serverless?.project_id;
this.logger = initializerContext.logger.get();
}

Expand Down Expand Up @@ -77,6 +82,10 @@ export class CloudPlugin implements Plugin<CloudSetup> {
trialEndDate: trialEndDate ? new Date(trialEndDate) : undefined,
isElasticStaffOwned,
isCloudEnabled: this.isCloudEnabled,
isServerlessEnabled: this.isServerlessEnabled,
serverless: {
projectId: this.config.serverless?.project_id,
},
registerCloudService: (contextProvider) => {
this.contextProviders.push(contextProvider);
},
Expand Down Expand Up @@ -118,6 +127,10 @@ export class CloudPlugin implements Plugin<CloudSetup> {
organizationUrl,
elasticsearchUrl: decodedId?.elasticsearchUrl,
kibanaUrl: decodedId?.kibanaUrl,
isServerlessEnabled: this.isServerlessEnabled,
serverless: {
projectId: this.config.serverless?.project_id,
},
};
}

Expand Down
30 changes: 30 additions & 0 deletions x-pack/plugins/cloud/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,21 @@ export interface CloudStart {
* The full URL to the Kibana deployment.
*/
kibanaUrl?: string;
/**
* `true` when running on Serverless Elastic Cloud
* Note that `isCloudEnabled` will always be true when `isServerlessEnabled` is.
*/
isServerlessEnabled: boolean;
/**
* Serverless configuration
*/
serverless: {
/**
* The serverless projectId.
* Will always be present if `isServerlessEnabled` is `true`
*/
projectId?: string;
};
}

export interface CloudSetup {
Expand Down Expand Up @@ -112,4 +127,19 @@ export interface CloudSetup {
* @param contextProvider The React component from the Service Provider.
*/
registerCloudService: (contextProvider: FC) => void;
/**
* `true` when running on Serverless Elastic Cloud
* Note that `isCloudEnabled` will always be true when `isServerlessEnabled` is.
*/
isServerlessEnabled: boolean;
/**
* Serverless configuration
*/
serverless: {
/**
* The serverless projectId.
* Will always be present if `isServerlessEnabled` is `true`
*/
projectId?: string;
};
}
8 changes: 8 additions & 0 deletions x-pack/plugins/cloud/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ const configSchema = schema.object({
profile_url: schema.maybe(schema.string()),
trial_end_date: schema.maybe(schema.string()),
is_elastic_staff_owned: schema.maybe(schema.boolean()),
serverless: schema.maybe(
schema.object({
project_id: schema.string(),
})
),
});

export type CloudConfigType = TypeOf<typeof configSchema>;
Expand All @@ -44,6 +49,9 @@ export const config: PluginConfigDescriptor<CloudConfigType> = {
profile_url: true,
trial_end_date: true,
is_elastic_staff_owned: true,
serverless: {
project_id: true,
},
},
schema: configSchema,
};
4 changes: 4 additions & 0 deletions x-pack/plugins/cloud/server/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ function createSetupMock(): jest.Mocked<CloudSetup> {
url: undefined,
secretToken: undefined,
},
isServerlessEnabled: false,
serverless: {
projectId: undefined,
},
};
}

Expand Down
31 changes: 30 additions & 1 deletion x-pack/plugins/cloud/server/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { decodeCloudIdMock, parseDeploymentIdFromDeploymentUrlMock } from './plugin.test.mocks';
import { coreMock } from '@kbn/core/server/mocks';
import type { CloudConfigType } from './config';
import { CloudPlugin } from './plugin';
import type { DecodedCloudId } from '../common/decode_cloud_id';

Expand All @@ -23,11 +24,12 @@ describe('Cloud Plugin', () => {
decodeCloudIdMock.mockReset().mockReturnValue({});
});

const setupPlugin = () => {
const setupPlugin = (configParts: Partial<CloudConfigType> = {}) => {
const initContext = coreMock.createPluginInitializerContext({
...baseConfig,
id: 'cloudId',
cname: 'cloud.elastic.co',
...configParts,
});
const plugin = new CloudPlugin(initContext);

Expand Down Expand Up @@ -90,6 +92,33 @@ describe('Cloud Plugin', () => {
expect(decodeCloudIdMock).toHaveBeenCalledTimes(1);
expect(decodeCloudIdMock).toHaveBeenCalledWith('cloudId', expect.any(Object));
});

describe('isServerlessEnabled', () => {
it('is `true` when `serverless.projectId` is set', () => {
const { setup } = setupPlugin({
serverless: {
project_id: 'my-awesome-project',
},
});
expect(setup.isServerlessEnabled).toBe(true);
});

it('is `false` when `serverless.projectId` is not set', () => {
const { setup } = setupPlugin({
serverless: undefined,
});
expect(setup.isServerlessEnabled).toBe(false);
});
});

it('exposes `serverless.projectId`', () => {
const { setup } = setupPlugin({
serverless: {
project_id: 'my-awesome-project',
},
});
expect(setup.serverless.projectId).toBe('my-awesome-project');
});
});
});

Expand Down
21 changes: 21 additions & 0 deletions x-pack/plugins/cloud/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,21 @@ export interface CloudSetup {
url?: string;
secretToken?: string;
};
/**
* `true` when running on Serverless Elastic Cloud
* Note that `isCloudEnabled` will always be true when `isServerlessEnabled` is.
*/
isServerlessEnabled: boolean;
/**
* Serverless configuration
*/
serverless: {
/**
* The serverless projectId.
* Will always be present if `isServerlessEnabled` is `true`
*/
projectId?: string;
};
}

/**
Expand All @@ -94,6 +109,8 @@ export class CloudPlugin implements Plugin<CloudSetup, CloudStart> {

public setup(core: CoreSetup, { usageCollection }: PluginsSetup): CloudSetup {
const isCloudEnabled = getIsCloudEnabled(this.config.id);
const isServerlessEnabled = !!this.config.serverless?.project_id;

registerCloudDeploymentMetadataAnalyticsContext(core.analytics, this.config);
registerCloudUsageCollector(usageCollection, {
isCloudEnabled,
Expand Down Expand Up @@ -121,6 +138,10 @@ export class CloudPlugin implements Plugin<CloudSetup, CloudStart> {
url: this.config.apm?.url,
secretToken: this.config.apm?.secret_token,
},
isServerlessEnabled,
serverless: {
projectId: this.config.serverless?.project_id,
},
};
}

Expand Down
Loading

0 comments on commit a9786df

Please sign in to comment.