Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose projectsUrl and baseUrl on the Cloud's plugin setup and start server-side contracts #163380

Merged
merged 10 commits into from
Aug 10, 2023
64 changes: 1 addition & 63 deletions x-pack/plugins/cloud/README.md
Original file line number Diff line number Diff line change
@@ -1,65 +1,3 @@
# `cloud` plugin

The `cloud` plugin adds Cloud-specific features to Kibana.

## Client-side API

The client-side plugin provides the following interface.

### `isCloudEnabled`

This is set to `true` for both ESS and ECE deployments.

### `cloudId`

This is the ID of the Cloud deployment to which the Kibana instance belongs.

**Example:** `eastus2.azure.elastic-cloud.com:9243$59ef636c6917463db140321484d63cfa$a8b109c08adc43279ef48f29af1a3911`

**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`.

### `baseUrl`

This is the URL of the Cloud interface.

**Example:** `https://cloud.elastic.co` (on the ESS production environment)

### `deploymentUrl`

This is the path to the Cloud deployment management page for the deployment to which the Kibana instance belongs. The value is already prepended with `baseUrl`.

**Example:** `{baseUrl}/deployments/bfdad4ef99a24212a06d387593686d63`

### `snapshotsUrl`

This is the path to the Snapshots page for the deployment to which the Kibana instance belongs. The value is already prepended with `deploymentUrl`.

**Example:** `{deploymentUrl}/elasticsearch/snapshots`

### `profileUrl`

This is the path to the Cloud User Profile page. The value is already prepended with `baseUrl`.

**Example:** `{baseUrl}/user/settings/`

### `organizationUrl`

This is the path to the Cloud Account and Billing page. The value is already prepended with `baseUrl`.

**Example:** `{baseUrl}/account/`

### `cname`

This value is the same as `baseUrl` on ESS but can be customized on ECE.

**Example:** `cloud.elastic.co` (on ESS)

### `trial_end_date`

The end date for the Elastic Cloud trial. Only available on Elastic Cloud.

**Example:** `2020-10-14T10:40:22Z`

### `is_elastic_staff_owned`

`true` if the deployment is owned by an Elastician. Only available on Elastic Cloud.
The `cloud` plugin adds Cloud-specific features to Kibana.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a chore I decided to include here: move all of these definitions onto the TS interfaces where they should be more useful and easier to keep in sync (i.e., be deleted or added when the code changes).

2 changes: 1 addition & 1 deletion x-pack/plugins/cloud/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { parseDeploymentIdFromDeploymentUrl } from '../common/parse_deployment_i
import { ELASTIC_SUPPORT_LINK, CLOUD_SNAPSHOTS_PATH } from '../common/constants';
import { decodeCloudId, type DecodedCloudId } from '../common/decode_cloud_id';
import type { CloudSetup, CloudStart } from './types';
import { getFullCloudUrl } from './utils';
import { getFullCloudUrl } from '../common/utils';

export interface CloudConfigType {
id?: string;
Expand Down
23 changes: 19 additions & 4 deletions x-pack/plugins/cloud/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ export interface CloudStart {
*/
cloudId?: string;
/**
* The full URL to the deployment management page on Elastic Cloud. Undefined if not running on Cloud.
* This is the path to the Cloud deployment management page for the deployment to which the Kibana instance belongs. The value is already prepended with `baseUrl`.
*
* @example `{baseUrl}/deployments/bfdad4ef99a24212a06d387593686d63`
*/
deploymentUrl?: string;
/**
Expand Down Expand Up @@ -84,6 +86,8 @@ export interface CloudSetup {
deploymentId?: string;
/**
* This value is the same as `baseUrl` on ESS but can be customized on ECE.
*
* @example `cloud.elastic.co`
*/
cname?: string;
/**
Expand All @@ -92,22 +96,30 @@ export interface CloudSetup {
baseUrl?: string;
/**
* The full URL to the deployment management page on Elastic Cloud. Undefined if not running on Cloud.
*
* @example `{baseUrl}/deployments/bfdad4ef99a24212a06d387593686d63`
*/
deploymentUrl?: string;
/**
* The full URL to the serverless projects page on Elastic Cloud. Undefined if not running in Serverless.
*/
projectsUrl?: string;
/**
* The full URL to the user profile page on Elastic Cloud. Undefined if not running on Cloud.
* This is the path to the Cloud User Profile page. The value is already prepended with `baseUrl`.
*
* @example `{baseUrl}/user/settings/`
*/
profileUrl?: string;
/**
* The full URL to the organization management page on Elastic Cloud. Undefined if not running on Cloud.
* This is the path to the Cloud Account and Billing page. The value is already prepended with `baseUrl`.
*
* @example `{baseUrl}/account/`
*/
organizationUrl?: string;
/**
* This is the path to the Snapshots page for the deployment to which the Kibana instance belongs. The value is already prepended with `deploymentUrl`.
*
* @example `{deploymentUrl}/elasticsearch/snapshots`
*/
snapshotsUrl?: string;
/**
Expand All @@ -131,7 +143,9 @@ export interface CloudSetup {
*/
isCloudEnabled: boolean;
/**
* When the Cloud Trial ends/ended for the organization that owns this deployment. Only available when running on Elastic Cloud.
* The end date for the Elastic Cloud trial. Only available on Elastic Cloud.
*
* @example `2020-10-14T10:40:22Z`
*/
trialEndDate?: Date;
/**
Expand All @@ -140,6 +154,7 @@ export interface CloudSetup {
isElasticStaffOwned?: boolean;
/**
* Registers CloudServiceProviders so start's `CloudContextProvider` hooks them.
*
* @param contextProvider The React component from the Service Provider.
*/
registerCloudService: (contextProvider: FC) => void;
Expand Down
34 changes: 34 additions & 0 deletions x-pack/plugins/cloud/server/__snapshots__/plugin.test.ts.snap

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion x-pack/plugins/cloud/server/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import type { CloudSetup } from './plugin';
import type { CloudSetup, CloudStart } from './plugin';

function createSetupMock(): jest.Mocked<CloudSetup> {
return {
Expand All @@ -19,6 +19,8 @@ function createSetupMock(): jest.Mocked<CloudSetup> {
isCloudEnabled: true,
isElasticStaffOwned: true,
trialEndDate: new Date('2020-10-01T14:13:12Z'),
projectsUrl: 'projects-url',
baseUrl: 'base-url',
apm: {
url: undefined,
secretToken: undefined,
Expand All @@ -30,6 +32,15 @@ function createSetupMock(): jest.Mocked<CloudSetup> {
};
}

function createStartMock(): jest.Mocked<CloudStart> {
return {
isCloudEnabled: true,
projectsUrl: 'projects-url',
baseUrl: 'base-url',
};
}

export const cloudMock = {
createSetup: createSetupMock,
createStart: createStartMock,
Comment on lines 43 to +45
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expose this missing mock for consumers of the cloud plugin

};
9 changes: 9 additions & 0 deletions x-pack/plugins/cloud/server/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const baseConfig = {
base_url: 'https://cloud.elastic.co',
deployment_url: '/abc123',
profile_url: '/user/settings/',
projects_url: '/projects/',
organization_url: '/account/',
};

Expand Down Expand Up @@ -42,6 +43,10 @@ describe('Cloud Plugin', () => {

describe('#setup', () => {
describe('interface', () => {
it('snapshot', () => {
const { setup } = setupPlugin();
expect(setup).toMatchSnapshot();
});
it('exposes isCloudEnabled', () => {
const { setup } = setupPlugin();
expect(setup.isCloudEnabled).toBe(true);
Expand Down Expand Up @@ -124,6 +129,10 @@ describe('Cloud Plugin', () => {

describe('#start', () => {
describe('interface', () => {
it('snapshot', () => {
const { start } = setupPlugin();
expect(start).toMatchSnapshot();
});
it('exposes isCloudEnabled', () => {
const { start } = setupPlugin();
expect(start.isCloudEnabled).toBe(true);
Expand Down
52 changes: 46 additions & 6 deletions x-pack/plugins/cloud/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { registerCloudUsageCollector } from './collectors';
import { getIsCloudEnabled } from '../common/is_cloud_enabled';
import { parseDeploymentIdFromDeploymentUrl } from '../common/parse_deployment_id_from_deployment_url';
import { decodeCloudId, DecodedCloudId } from '../common/decode_cloud_id';
import { getFullCloudUrl } from '../common/utils';
import { readInstanceSizeMb } from './env';

interface PluginsSetup {
jloleysens marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -25,7 +26,11 @@ interface PluginsSetup {
*/
export interface CloudSetup {
/**
* The deployment's Cloud ID. Only available when running on Elastic Cloud.
* This is the ID of the Cloud deployment to which the Kibana instance belongs.
*
* @example `eastus2.azure.elastic-cloud.com:9243$59ef636c6917463db140321484d63cfa$a8b109c08adc43279ef48f29af1a3911`
*
* @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;
/**
Expand All @@ -41,15 +46,27 @@ export interface CloudSetup {
*/
kibanaUrl?: string;
/**
* {host} from the deployment url https://<deploymentId>.<application>.<host><?:port>
* This is the URL to the "projects" interface on cloud.
*
* @example `https://cloud.elastic.co/projects`
*/
projectsUrl?: string;
Copy link
Contributor Author

@jloleysens jloleysens Aug 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at this naming I'm not convinced it is 100% correct as we default it to a path, not a URL in our serverless config (the typescript, not YAML) 🤔 , will it actually be a full URL in our serverless configuration? CC @Dosant

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if these should go under serverless property, provided that they are serverless specific.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I was also thinking that... It's currently not scoped that way in the cloud plugins' config.ts as there are a number of URL values not scoped to serverless key either... I think it may already be in use too so I figured we'd mirror that structure in our contract.

Don't feel strongly about it, so I'll go with your opinion if you disagree with my reasoning?

Copy link
Contributor

@gsoldevila gsoldevila Aug 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looked at each of the 2:

  • baseUrl: I'm fine with this one being top level. It exists for more than 2 years and does not seem to be serverless specific. Perhaps we could update the description in the config object, so that both match.
  • projectsUrl: Added yesterday through this PR. This one seems to be serverless specific, and since 8.10.0 hasn't been released yet, perhaps we can challenge the fact that it is not under the serverless object. CC @Dosant WDYT? BTW the property is not present in the README.md.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW the property is not present in the README.md.

sidenote on the readme: seems like we can capture this info in one place: the TS interface itself 😅

Copy link
Contributor

@Dosant Dosant Aug 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gsoldevila,

projectsUrl: Added yesterday through this PR. This one seems to be serverless specific, and since 8.10.0 hasn't been released yet, perhaps we can challenge the fact that it is not under the serverless object. CC @Dosant WDYT? BTW the property is not present in the README.md.

I don't mind, your call.
We will also need to update it in the project-controller.

The reason I didn't put it under serverless initially called out here:
https://github.com/elastic/kibana/pull/163076/files#r1283288347

I wanted to have the default value and be able to test it locally, but to add serverless.* config we require to also add serverless.projectid. So I couldn't just add serverless.project_url for testing without adding serverless.projectid

it also seemed fine to follow other URLs for simplicity. also I can imagine we might need projects_url in the future in non-project deployments

Copy link
Contributor

@gsoldevila gsoldevila Aug 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what the purpose of the project_url is, but by the name + description it would seem that it is exclusive to serverless.

FWIW Pierre made the project_id nullable, but still, if you want to test locally I'm afraid you'll need the project_id anyway, as it is this very property that we rely on to determine whether isServerlessEnabled:

https://github.com/elastic/kibana/blob/main/x-pack/plugins/cloud/server/plugin.ts#L112

Copy link
Contributor Author

@jloleysens jloleysens Aug 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH I can see @Dosant reasoning, one could argue this is under the concern of Cloud (since it is a cloud URL) I am happy with it living outside serverless -- i.e., leave it as it is now.

(also already being used in project controller)

Let's call it here and commit to placing any future values inside of serverless but leave cloud URLs where they are. WDYT @Dosant @gsoldevila ? I'll capture the decision in a comment on the interface.

Copy link
Contributor

@gsoldevila gsoldevila Aug 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, my brain read projectUrl (singular) as if it was a URL of a single project.
That's why for me it was obvious that it should go under serverless config.

/**
* This is the URL of the Cloud interface.
*
* @example `https://cloud.elastic.co` (on the ESS production environment)
*/
baseUrl?: string;
/**
* {host} of the deployment url https://<deploymentId>.<application>.<host><?:port>
*/
cloudHost?: string;
/**
* {port} from the deployment url https://<deploymentId>.<application>.<host><?:port>
* {port} of the deployment url https://<deploymentId>.<application>.<host><?:port>
*/
cloudDefaultPort?: string;
/**
* `true` when running on Elastic Cloud.
* This is set to `true` for both ESS and ECE deployments.
*/
isCloudEnabled: boolean;
/**
Expand Down Expand Up @@ -93,9 +110,21 @@ export interface CloudSetup {
*/
export interface CloudStart {
/**
* `true` when running on Elastic Cloud.
* This is set to `true` for both ESS and ECE deployments.
*/
isCloudEnabled: boolean;
/**
* This is the URL to the "projects" interface on cloud.
*
* @example `https://cloud.elastic.co/projects`
*/
projectsUrl?: string;
/**
* This is the URL of the Cloud interface.
*
* @example `https://cloud.elastic.co` (on the ESS production environment)
*/
baseUrl?: string;
}

export class CloudPlugin implements Plugin<CloudSetup, CloudStart> {
Expand Down Expand Up @@ -124,6 +153,7 @@ export class CloudPlugin implements Plugin<CloudSetup, CloudStart> {
}

return {
...this.getCloudUrls(),
cloudId: this.config.id,
instanceSizeMb: readInstanceSizeMb(),
deploymentId: parseDeploymentIdFromDeploymentUrl(this.config.deployment_url),
Expand All @@ -145,9 +175,19 @@ export class CloudPlugin implements Plugin<CloudSetup, CloudStart> {
};
}

public start() {
public start(): CloudStart {
return {
...this.getCloudUrls(),
isCloudEnabled: getIsCloudEnabled(this.config.id),
};
}

private getCloudUrls() {
const { base_url: baseUrl } = this.config;
const projectsUrl = getFullCloudUrl(baseUrl, this.config.projects_url);
return {
baseUrl,
projectsUrl,
};
}
}