[Cloud] Extend metadata (#189001)

This commit is contained in:
Alejandro Fernández Haro 2024-07-24 14:10:28 +02:00 committed by GitHub
parent 850aa69648
commit 56fc5ce8e1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 129 additions and 11 deletions

View file

@ -487,7 +487,7 @@ The plugin exposes the static DefaultEditorController class to consume.
|{kib-repo}blob/{branch}/x-pack/plugins/cloud/README.md[cloud]
|The cloud plugin adds Cloud-specific features to Kibana.
|The cloud plugin exposes Cloud-specific metadata to Kibana.
|{kib-repo}blob/{branch}/x-pack/plugins/cloud_integrations/cloud_chat/README.md[cloudChat]

View file

@ -246,6 +246,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
'xpack.cloud_integrations.full_story.eventTypesAllowlist (array)',
'xpack.cloud_integrations.full_story.pageVarsDebounceTime (duration)',
'xpack.cloud.id (string)',
'xpack.cloud.organization_id (string)',
'xpack.cloud.organization_url (string)',
'xpack.cloud.billing_url (string)',
'xpack.cloud.profile_url (string)',
@ -256,6 +257,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
'xpack.cloud.serverless.project_id (string)',
'xpack.cloud.serverless.project_name (string)',
'xpack.cloud.serverless.project_type (string)',
'xpack.cloud.serverless.orchestrator_target (string)',
'xpack.cloud.onboarding.default_solution (string)',
'xpack.discoverEnhanced.actions.exploreDataInChart.enabled (boolean)',
'xpack.discoverEnhanced.actions.exploreDataInContextMenu.enabled (boolean)',

View file

@ -1,3 +1,3 @@
# `cloud` plugin
The `cloud` plugin adds Cloud-specific features to Kibana.
The `cloud` plugin exposes Cloud-specific metadata to Kibana.

View file

@ -11,12 +11,14 @@ import { parseDeploymentIdFromDeploymentUrl } from './parse_deployment_id_from_d
export interface CloudDeploymentMetadata {
id?: string;
organization_id?: string;
trial_end_date?: string;
is_elastic_staff_owned?: boolean;
deployment_url?: string;
serverless?: {
project_id?: string;
project_type?: string;
orchestrator_target?: string;
};
}
@ -29,26 +31,40 @@ export function registerCloudDeploymentMetadataAnalyticsContext(
}
const {
id: cloudId,
organization_id: organizationId,
trial_end_date: cloudTrialEndDate,
is_elastic_staff_owned: cloudIsElasticStaffOwned,
serverless: { project_id: projectId, project_type: projectType } = {},
serverless: {
project_id: projectId,
project_type: projectType,
orchestrator_target: orchestratorTarget,
} = {},
} = cloudMetadata;
analytics.registerContextProvider({
name: 'Cloud Deployment Metadata',
context$: of({
cloudId,
organizationId,
deploymentId: parseDeploymentIdFromDeploymentUrl(cloudMetadata.deployment_url),
cloudTrialEndDate,
cloudIsElasticStaffOwned,
projectId,
projectType,
orchestratorTarget,
}),
schema: {
cloudId: {
type: 'keyword',
_meta: { description: 'The Cloud ID' },
},
organizationId: {
type: 'keyword',
_meta: {
description: 'The Elastic Cloud Organization ID that owns the deployment/project',
optional: true,
},
},
deploymentId: {
type: 'keyword',
_meta: { description: 'The Deployment ID', optional: true },
@ -72,6 +88,13 @@ export function registerCloudDeploymentMetadataAnalyticsContext(
type: 'keyword',
_meta: { description: 'The Serverless Project type', optional: true },
},
orchestratorTarget: {
type: 'keyword',
_meta: {
description: 'The Orchestrator Target where it is deployed (canary/non-canary)',
optional: true,
},
},
},
});
}

View file

@ -21,6 +21,7 @@ import { getSupportUrl } from './utils';
export interface CloudConfigType {
id?: string;
organization_id?: string;
cname?: string;
base_url?: string;
profile_url?: string;
@ -40,6 +41,7 @@ export interface CloudConfigType {
project_id: string;
project_name?: string;
project_type?: string;
orchestrator_target?: string;
};
}
@ -89,6 +91,7 @@ export class CloudPlugin implements Plugin<CloudSetup> {
return {
cloudId: id,
organizationId: this.config.organization_id,
deploymentId: parseDeploymentIdFromDeploymentUrl(this.config.deployment_url),
cname,
baseUrl,
@ -108,6 +111,7 @@ export class CloudPlugin implements Plugin<CloudSetup> {
projectId: this.config.serverless?.project_id,
projectName: this.config.serverless?.project_name,
projectType: this.config.serverless?.project_type,
orchestratorTarget: this.config.serverless?.orchestrator_target,
},
registerCloudService: (contextProvider) => {
this.contextProviders.push(contextProvider);

View file

@ -97,6 +97,10 @@ export interface CloudSetup {
* Cloud ID. Undefined if not running on Cloud.
*/
cloudId?: string;
/**
* The Elastic Cloud Organization that owns this deployment/project.
*/
organizationId?: string;
/**
* The deployment's ID. Only available when running on Elastic Cloud.
*/
@ -208,5 +212,10 @@ export interface CloudSetup {
* Will always be present if `isServerlessEnabled` is `true`
*/
projectType?: string;
/**
* The serverless orchestrator target. The potential values are `canary` or `non-canary`
* Will always be present if `isServerlessEnabled` is `true`
*/
orchestratorTarget?: string;
};
}

View file

@ -20,8 +20,10 @@ Object {
"onboarding": Object {
"defaultSolution": undefined,
},
"organizationId": undefined,
"projectsUrl": "https://cloud.elastic.co/projects/",
"serverless": Object {
"orchestratorTarget": undefined,
"projectId": undefined,
"projectName": undefined,
"projectType": undefined,

View file

@ -38,10 +38,12 @@ describe('createCloudUsageCollector', () => {
expect(await collector.fetch(collectorFetchContext)).toStrictEqual({
isCloudEnabled: true,
isElasticStaffOwned: undefined,
organizationId: undefined,
trialEndDate: undefined,
deploymentId: undefined,
projectId: undefined,
projectType: undefined,
orchestratorTarget: undefined,
});
});
@ -54,11 +56,13 @@ describe('createCloudUsageCollector', () => {
expect(await collector.fetch(collectorFetchContext)).toStrictEqual({
isCloudEnabled: true,
isElasticStaffOwned: undefined,
organizationId: undefined,
trialEndDate: '2020-10-01T14:30:16Z',
inTrial: false,
deploymentId: undefined,
projectId: undefined,
projectType: undefined,
orchestratorTarget: undefined,
});
});
@ -67,9 +71,11 @@ describe('createCloudUsageCollector', () => {
isCloudEnabled: true,
trialEndDate: '2020-10-01T14:30:16Z',
isElasticStaffOwned: true,
organizationId: '1234',
deploymentId: 'a-deployment-id',
projectId: 'a-project-id',
projectType: 'security',
orchestratorTarget: 'canary',
});
expect(await collector.fetch(collectorFetchContext)).toStrictEqual({
@ -77,9 +83,11 @@ describe('createCloudUsageCollector', () => {
trialEndDate: '2020-10-01T14:30:16Z',
inTrial: false,
isElasticStaffOwned: true,
organizationId: '1234',
deploymentId: 'a-deployment-id',
projectId: 'a-project-id',
projectType: 'security',
orchestratorTarget: 'canary',
});
});
});

View file

@ -12,9 +12,11 @@ export interface CloudUsageCollectorConfig {
// Using * | undefined instead of ?: to force the calling code to list all the options (even when they can be undefined)
trialEndDate: string | undefined;
isElasticStaffOwned: boolean | undefined;
organizationId: string | undefined;
deploymentId: string | undefined;
projectId: string | undefined;
projectType: string | undefined;
orchestratorTarget: string | undefined;
}
interface CloudUsage {
@ -22,9 +24,11 @@ interface CloudUsage {
trialEndDate?: string;
inTrial?: boolean;
isElasticStaffOwned?: boolean;
organizationId?: string;
deploymentId?: string;
projectId?: string;
projectType?: string;
orchestratorTarget?: string;
}
export function createCloudUsageCollector(
@ -35,19 +39,36 @@ export function createCloudUsageCollector(
isCloudEnabled,
trialEndDate,
isElasticStaffOwned,
organizationId,
deploymentId,
projectId,
projectType,
orchestratorTarget,
} = config;
const trialEndDateMs = trialEndDate ? new Date(trialEndDate).getTime() : undefined;
return usageCollection.makeUsageCollector<CloudUsage>({
type: 'cloud',
isReady: () => true,
schema: {
isCloudEnabled: { type: 'boolean' },
trialEndDate: { type: 'date' },
inTrial: { type: 'boolean' },
isElasticStaffOwned: { type: 'boolean' },
isCloudEnabled: {
type: 'boolean',
_meta: { description: 'Is the deployment running in Elastic Cloud (ESS or Serverless)?' },
},
trialEndDate: { type: 'date', _meta: { description: 'End of the trial period' } },
inTrial: {
type: 'boolean',
_meta: { description: 'Is the organization during the trial period?' },
},
isElasticStaffOwned: {
type: 'boolean',
_meta: { description: 'Is the deploymend owned by an Elastician' },
},
organizationId: {
type: 'keyword',
_meta: {
description: 'The Elastic Cloud Organization ID that owns the deployment/project',
},
},
deploymentId: {
type: 'keyword',
_meta: { description: 'The ESS Deployment ID' },
@ -60,16 +81,22 @@ export function createCloudUsageCollector(
type: 'keyword',
_meta: { description: 'The Serverless Project type' },
},
orchestratorTarget: {
type: 'keyword',
_meta: { description: 'The Orchestrator Target where it is deployed (canary/non-canary)' },
},
},
fetch: () => {
return {
isCloudEnabled,
isElasticStaffOwned,
organizationId,
trialEndDate,
...(trialEndDateMs ? { inTrial: Date.now() <= trialEndDateMs } : {}),
deploymentId,
projectId,
projectType,
orchestratorTarget,
};
},
});

View file

@ -25,6 +25,7 @@ const configSchema = schema.object({
deployments_url: schema.string({ defaultValue: '/deployments' }),
deployment_url: schema.maybe(schema.string()),
id: schema.maybe(schema.string()),
organization_id: schema.maybe(schema.string()),
billing_url: schema.maybe(schema.string()),
performance_url: schema.maybe(schema.string()),
users_and_roles_url: schema.maybe(schema.string()),
@ -44,6 +45,7 @@ const configSchema = schema.object({
project_id: schema.maybe(schema.string()),
project_name: schema.maybe(schema.string()),
project_type: schema.maybe(schema.string()),
orchestrator_target: schema.maybe(schema.string()),
},
// avoid future chicken-and-egg situation with the component populating the config
{ unknowns: 'ignore' }
@ -60,6 +62,7 @@ export const config: PluginConfigDescriptor<CloudConfigType> = {
deployments_url: true,
deployment_url: true,
id: true,
organization_id: true,
billing_url: true,
users_and_roles_url: true,
performance_url: true,
@ -72,6 +75,7 @@ export const config: PluginConfigDescriptor<CloudConfigType> = {
project_id: true,
project_name: true,
project_type: true,
orchestrator_target: true,
},
onboarding: {
default_solution: true,

View file

@ -35,6 +35,10 @@ export interface CloudSetup {
* @note The `cloudId` is a concatenation of the deployment name and a hash. Users can update the deployment name, changing the `cloudId`. However, the changed `cloudId` will not be re-injected into `kibana.yml`. If you need the current `cloudId` the best approach is to split the injected `cloudId` on the semi-colon, and replace the first element with the `persistent.cluster.metadata.display_name` value as provided by a call to `GET _cluster/settings`.
*/
cloudId?: string;
/**
* The Elastic Cloud Organization that owns this deployment/project.
*/
organizationId?: string;
/**
* The deployment's ID. Only available when running on Elastic Cloud.
*/
@ -127,6 +131,11 @@ export interface CloudSetup {
* Will always be present if `isServerlessEnabled` is `true`
*/
projectType?: string;
/**
* The serverless orchestrator target. The potential values are `canary` or `non-canary`
* Will always be present if `isServerlessEnabled` is `true`
*/
orchestratorTarget?: string;
};
}
@ -163,19 +172,23 @@ export class CloudPlugin implements Plugin<CloudSetup, CloudStart> {
public setup(core: CoreSetup, { usageCollection }: PluginsSetup): CloudSetup {
const isCloudEnabled = getIsCloudEnabled(this.config.id);
const organizationId = this.config.organization_id;
const projectId = this.config.serverless?.project_id;
const projectType = this.config.serverless?.project_type;
const orchestratorTarget = this.config.serverless?.orchestrator_target;
const isServerlessEnabled = !!projectId;
const deploymentId = parseDeploymentIdFromDeploymentUrl(this.config.deployment_url);
registerCloudDeploymentMetadataAnalyticsContext(core.analytics, this.config);
registerCloudUsageCollector(usageCollection, {
isCloudEnabled,
organizationId,
trialEndDate: this.config.trial_end_date,
isElasticStaffOwned: this.config.is_elastic_staff_owned,
deploymentId,
projectId,
projectType,
orchestratorTarget,
});
let decodedId: DecodedCloudId | undefined;
@ -186,6 +199,7 @@ export class CloudPlugin implements Plugin<CloudSetup, CloudStart> {
return {
...this.getCloudUrls(),
cloudId: this.config.id,
organizationId,
instanceSizeMb: readInstanceSizeMb(),
deploymentId,
elasticsearchUrl: decodedId?.elasticsearchUrl,
@ -207,6 +221,7 @@ export class CloudPlugin implements Plugin<CloudSetup, CloudStart> {
projectId,
projectName: this.config.serverless?.project_name,
projectType,
orchestratorTarget,
},
};
}

View file

@ -7100,16 +7100,34 @@
"cloud": {
"properties": {
"isCloudEnabled": {
"type": "boolean"
"type": "boolean",
"_meta": {
"description": "Is the deployment running in Elastic Cloud (ESS or Serverless)?"
}
},
"trialEndDate": {
"type": "date"
"type": "date",
"_meta": {
"description": "End of the trial period"
}
},
"inTrial": {
"type": "boolean"
"type": "boolean",
"_meta": {
"description": "Is the organization during the trial period?"
}
},
"isElasticStaffOwned": {
"type": "boolean"
"type": "boolean",
"_meta": {
"description": "Is the deploymend owned by an Elastician"
}
},
"organizationId": {
"type": "keyword",
"_meta": {
"description": "The Elastic Cloud Organization ID that owns the deployment/project"
}
},
"deploymentId": {
"type": "keyword",
@ -7128,6 +7146,12 @@
"_meta": {
"description": "The Serverless Project type"
}
},
"orchestratorTarget": {
"type": "keyword",
"_meta": {
"description": "The Orchestrator Target where it is deployed (canary/non-canary)"
}
}
}
},