backend-common: move HostDiscovery to backend-app-api
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-common': patch
|
||||
---
|
||||
|
||||
The `HostDiscovery` export has been deprecated, import it from `@backstage/backend-app-api` instead.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-test-utils': patch
|
||||
---
|
||||
|
||||
Updated to import `HostDiscovery` from `@backstage/backend-app-api`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-app-api': patch
|
||||
---
|
||||
|
||||
Moved `HostDiscovery` from `@backstage/backend-common`.
|
||||
@@ -10,6 +10,7 @@ import { BackendFeature } from '@backstage/backend-plugin-api';
|
||||
import { CacheClient } from '@backstage/backend-common';
|
||||
import { Config } from '@backstage/config';
|
||||
import { CorsOptions } from 'cors';
|
||||
import { DiscoveryService } from '@backstage/backend-plugin-api';
|
||||
import { ErrorRequestHandler } from 'express';
|
||||
import { Express as Express_2 } from 'express';
|
||||
import { Format } from 'logform';
|
||||
@@ -25,7 +26,6 @@ import { LoadConfigOptionsRemote } from '@backstage/config-loader';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { PermissionsService } from '@backstage/backend-plugin-api';
|
||||
import { PluginDatabaseManager } from '@backstage/backend-common';
|
||||
import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
import { RemoteConfigSourceOptions } from '@backstage/config-loader';
|
||||
import { RequestHandler } from 'express';
|
||||
import { RequestListener } from 'http';
|
||||
@@ -114,7 +114,7 @@ export interface DefaultRootHttpRouterOptions {
|
||||
|
||||
// @public (undocumented)
|
||||
export const discoveryServiceFactory: () => ServiceFactory<
|
||||
PluginEndpointDiscovery,
|
||||
DiscoveryService,
|
||||
'plugin'
|
||||
>;
|
||||
|
||||
@@ -128,6 +128,20 @@ export interface ExtendedHttpServer extends http.Server {
|
||||
stop(): Promise<void>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export class HostDiscovery implements DiscoveryService {
|
||||
static fromConfig(
|
||||
config: Config,
|
||||
options?: {
|
||||
basePath?: string;
|
||||
},
|
||||
): HostDiscovery;
|
||||
// (undocumented)
|
||||
getBaseUrl(pluginId: string): Promise<string>;
|
||||
// (undocumented)
|
||||
getExternalBaseUrl(pluginId: string): Promise<string>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface HttpRouterFactoryOptions {
|
||||
getPath?(pluginId: string): string;
|
||||
|
||||
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export interface Config {
|
||||
/** Discovery options. */
|
||||
discovery?: {
|
||||
/**
|
||||
* Endpoints
|
||||
*
|
||||
* A list of target baseUrls and the associated plugins.
|
||||
*/
|
||||
endpoints: {
|
||||
/**
|
||||
* The target baseUrl to use for the plugin
|
||||
*
|
||||
* Can be either a string or an object with internal and external keys.
|
||||
* Targets with `{{pluginId}}` or `{{ pluginId }} in the url will be replaced with the pluginId.
|
||||
*/
|
||||
target: string | { internal: string; external: string };
|
||||
/** Array of plugins which use the target baseUrl. */
|
||||
plugins: string[];
|
||||
}[];
|
||||
};
|
||||
}
|
||||
@@ -90,8 +90,10 @@
|
||||
"mock-fs": "^5.2.0",
|
||||
"supertest": "^6.1.3"
|
||||
},
|
||||
"configSchema": "config.d.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"config.d.ts",
|
||||
"alpha"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Config } from '@backstage/config';
|
||||
import { readHttpServerOptions } from '@backstage/backend-app-api';
|
||||
import { DiscoveryService } from '@backstage/backend-plugin-api';
|
||||
|
||||
type Target = string | { internal: string; external: string };
|
||||
|
||||
/**
|
||||
* HostDiscovery is a basic PluginEndpointDiscovery implementation
|
||||
* that can handle plugins that are hosted in a single or multiple deployments.
|
||||
*
|
||||
* The deployment may be scaled horizontally, as long as the external URL
|
||||
* is the same for all instances. However, internal URLs will always be
|
||||
* resolved to the same host, so there won't be any balancing of internal traffic.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class HostDiscovery implements DiscoveryService {
|
||||
/**
|
||||
* Creates a new HostDiscovery discovery instance by reading
|
||||
* from the `backend` config section, specifically the `.baseUrl` for
|
||||
* discovering the external URL, and the `.listen` and `.https` config
|
||||
* for the internal one.
|
||||
*
|
||||
* Can be overridden in config by providing a target and corresponding plugins in `discovery.endpoints`.
|
||||
* eg.
|
||||
* ```yaml
|
||||
* discovery:
|
||||
* endpoints:
|
||||
* - target: https://internal.example.com/internal-catalog
|
||||
* plugins: [catalog]
|
||||
* - target: https://internal.example.com/secure/api/{{pluginId}}
|
||||
* plugins: [auth, permission]
|
||||
* - target:
|
||||
* internal: https://internal.example.com/search
|
||||
* external: https://example.com/search
|
||||
* plugins: [search]
|
||||
* ```
|
||||
*
|
||||
* The basePath defaults to `/api`, meaning the default full internal
|
||||
* path for the `catalog` plugin will be `http://localhost:7007/api/catalog`.
|
||||
*/
|
||||
static fromConfig(config: Config, options?: { basePath?: string }) {
|
||||
const basePath = options?.basePath ?? '/api';
|
||||
const externalBaseUrl = config
|
||||
.getString('backend.baseUrl')
|
||||
.replace(/\/+$/, '');
|
||||
|
||||
const {
|
||||
listen: { host: listenHost = '::', port: listenPort },
|
||||
} = readHttpServerOptions(config.getConfig('backend'));
|
||||
const protocol = config.has('backend.https') ? 'https' : 'http';
|
||||
|
||||
// Translate bind-all to localhost, and support IPv6
|
||||
let host = listenHost;
|
||||
if (host === '::' || host === '') {
|
||||
// We use localhost instead of ::1, since IPv6-compatible systems should default
|
||||
// to using IPv6 when they see localhost, but if the system doesn't support IPv6
|
||||
// things will still work.
|
||||
host = 'localhost';
|
||||
} else if (host === '0.0.0.0') {
|
||||
host = '127.0.0.1';
|
||||
}
|
||||
if (host.includes(':')) {
|
||||
host = `[${host}]`;
|
||||
}
|
||||
|
||||
const internalBaseUrl = `${protocol}://${host}:${listenPort}`;
|
||||
|
||||
return new HostDiscovery(
|
||||
internalBaseUrl + basePath,
|
||||
externalBaseUrl + basePath,
|
||||
config.getOptionalConfig('discovery'),
|
||||
);
|
||||
}
|
||||
|
||||
private constructor(
|
||||
private readonly internalBaseUrl: string,
|
||||
private readonly externalBaseUrl: string,
|
||||
private readonly discoveryConfig: Config | undefined,
|
||||
) {}
|
||||
|
||||
private getTargetFromConfig(pluginId: string, type: 'internal' | 'external') {
|
||||
const endpoints = this.discoveryConfig?.getOptionalConfigArray('endpoints');
|
||||
|
||||
const target = endpoints
|
||||
?.find(endpoint => endpoint.getStringArray('plugins').includes(pluginId))
|
||||
?.get<Target>('target');
|
||||
|
||||
if (!target) {
|
||||
const baseUrl =
|
||||
type === 'external' ? this.externalBaseUrl : this.internalBaseUrl;
|
||||
|
||||
return `${baseUrl}/${encodeURIComponent(pluginId)}`;
|
||||
}
|
||||
|
||||
if (typeof target === 'string') {
|
||||
return target.replace(
|
||||
/\{\{\s*pluginId\s*\}\}/g,
|
||||
encodeURIComponent(pluginId),
|
||||
);
|
||||
}
|
||||
|
||||
return target[type].replace(
|
||||
/\{\{\s*pluginId\s*\}\}/g,
|
||||
encodeURIComponent(pluginId),
|
||||
);
|
||||
}
|
||||
|
||||
async getBaseUrl(pluginId: string): Promise<string> {
|
||||
return this.getTargetFromConfig(pluginId, 'internal');
|
||||
}
|
||||
|
||||
async getExternalBaseUrl(pluginId: string): Promise<string> {
|
||||
return this.getTargetFromConfig(pluginId, 'external');
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -14,11 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { HostDiscovery } from '@backstage/backend-common';
|
||||
import {
|
||||
coreServices,
|
||||
createServiceFactory,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { HostDiscovery } from './HostDiscovery';
|
||||
|
||||
/** @public */
|
||||
export const discoveryServiceFactory = createServiceFactory({
|
||||
|
||||
@@ -15,3 +15,4 @@
|
||||
*/
|
||||
|
||||
export { discoveryServiceFactory } from './discoveryServiceFactory';
|
||||
export { HostDiscovery } from './HostDiscovery';
|
||||
|
||||
@@ -28,6 +28,7 @@ import { GiteaIntegration } from '@backstage/integration';
|
||||
import { GithubCredentialsProvider } from '@backstage/integration';
|
||||
import { GithubIntegration } from '@backstage/integration';
|
||||
import { GitLabIntegration } from '@backstage/integration';
|
||||
import { HostDiscovery as HostDiscovery_2 } from '@backstage/backend-app-api';
|
||||
import { IdentityService } from '@backstage/backend-plugin-api';
|
||||
import { isChildPath } from '@backstage/cli-common';
|
||||
import { Knex } from 'knex';
|
||||
@@ -475,18 +476,7 @@ export class GitlabUrlReader implements UrlReader {
|
||||
}
|
||||
|
||||
// @public
|
||||
export class HostDiscovery implements PluginEndpointDiscovery {
|
||||
static fromConfig(
|
||||
config: Config,
|
||||
options?: {
|
||||
basePath?: string;
|
||||
},
|
||||
): HostDiscovery;
|
||||
// (undocumented)
|
||||
getBaseUrl(pluginId: string): Promise<string>;
|
||||
// (undocumented)
|
||||
getExternalBaseUrl(pluginId: string): Promise<string>;
|
||||
}
|
||||
export const HostDiscovery: typeof HostDiscovery_2;
|
||||
|
||||
export { isChildPath };
|
||||
|
||||
@@ -747,7 +737,7 @@ export type ServiceBuilder = {
|
||||
export function setRootLogger(newLogger: winston.Logger): void;
|
||||
|
||||
// @public @deprecated
|
||||
export const SingleHostDiscovery: typeof HostDiscovery;
|
||||
export const SingleHostDiscovery: typeof HostDiscovery_2;
|
||||
|
||||
// @public
|
||||
export type StatusCheck = () => Promise<any>;
|
||||
|
||||
Vendored
-20
@@ -216,24 +216,4 @@ export interface Config {
|
||||
*/
|
||||
csp?: { [policyId: string]: string[] | false };
|
||||
};
|
||||
|
||||
/** Discovery options. */
|
||||
discovery?: {
|
||||
/**
|
||||
* Endpoints
|
||||
*
|
||||
* A list of target baseUrls and the associated plugins.
|
||||
*/
|
||||
endpoints: {
|
||||
/**
|
||||
* The target baseUrl to use for the plugin
|
||||
*
|
||||
* Can be either a string or an object with internal and external keys.
|
||||
* Targets with `{{pluginId}}` or `{{ pluginId }} in the url will be replaced with the pluginId.
|
||||
*/
|
||||
target: string | { internal: string; external: string };
|
||||
/** Array of plugins which use the target baseUrl. */
|
||||
plugins: string[];
|
||||
}[];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,11 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Config } from '@backstage/config';
|
||||
import { PluginEndpointDiscovery } from './types';
|
||||
import { readHttpServerOptions } from '@backstage/backend-app-api';
|
||||
import { HostDiscovery as _HostDiscovery } from '@backstage/backend-app-api';
|
||||
|
||||
type Target = string | { internal: string; external: string };
|
||||
export type { DiscoveryService as PluginEndpointDiscovery } from '@backstage/backend-plugin-api';
|
||||
|
||||
/**
|
||||
* HostDiscovery is a basic PluginEndpointDiscovery implementation
|
||||
@@ -30,106 +28,7 @@ type Target = string | { internal: string; external: string };
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class HostDiscovery implements PluginEndpointDiscovery {
|
||||
/**
|
||||
* Creates a new HostDiscovery discovery instance by reading
|
||||
* from the `backend` config section, specifically the `.baseUrl` for
|
||||
* discovering the external URL, and the `.listen` and `.https` config
|
||||
* for the internal one.
|
||||
*
|
||||
* Can be overridden in config by providing a target and corresponding plugins in `discovery.endpoints`.
|
||||
* eg.
|
||||
* ```yaml
|
||||
* discovery:
|
||||
* endpoints:
|
||||
* - target: https://internal.example.com/internal-catalog
|
||||
* plugins: [catalog]
|
||||
* - target: https://internal.example.com/secure/api/{{pluginId}}
|
||||
* plugins: [auth, permission]
|
||||
* - target:
|
||||
* internal: https://internal.example.com/search
|
||||
* external: https://example.com/search
|
||||
* plugins: [search]
|
||||
* ```
|
||||
*
|
||||
* The basePath defaults to `/api`, meaning the default full internal
|
||||
* path for the `catalog` plugin will be `http://localhost:7007/api/catalog`.
|
||||
*/
|
||||
static fromConfig(config: Config, options?: { basePath?: string }) {
|
||||
const basePath = options?.basePath ?? '/api';
|
||||
const externalBaseUrl = config
|
||||
.getString('backend.baseUrl')
|
||||
.replace(/\/+$/, '');
|
||||
|
||||
const {
|
||||
listen: { host: listenHost = '::', port: listenPort },
|
||||
} = readHttpServerOptions(config.getConfig('backend'));
|
||||
const protocol = config.has('backend.https') ? 'https' : 'http';
|
||||
|
||||
// Translate bind-all to localhost, and support IPv6
|
||||
let host = listenHost;
|
||||
if (host === '::' || host === '') {
|
||||
// We use localhost instead of ::1, since IPv6-compatible systems should default
|
||||
// to using IPv6 when they see localhost, but if the system doesn't support IPv6
|
||||
// things will still work.
|
||||
host = 'localhost';
|
||||
} else if (host === '0.0.0.0') {
|
||||
host = '127.0.0.1';
|
||||
}
|
||||
if (host.includes(':')) {
|
||||
host = `[${host}]`;
|
||||
}
|
||||
|
||||
const internalBaseUrl = `${protocol}://${host}:${listenPort}`;
|
||||
|
||||
return new HostDiscovery(
|
||||
internalBaseUrl + basePath,
|
||||
externalBaseUrl + basePath,
|
||||
config.getOptionalConfig('discovery'),
|
||||
);
|
||||
}
|
||||
|
||||
private constructor(
|
||||
private readonly internalBaseUrl: string,
|
||||
private readonly externalBaseUrl: string,
|
||||
private readonly discoveryConfig: Config | undefined,
|
||||
) {}
|
||||
|
||||
private getTargetFromConfig(pluginId: string, type: 'internal' | 'external') {
|
||||
const endpoints = this.discoveryConfig?.getOptionalConfigArray('endpoints');
|
||||
|
||||
const target = endpoints
|
||||
?.find(endpoint => endpoint.getStringArray('plugins').includes(pluginId))
|
||||
?.get<Target>('target');
|
||||
|
||||
if (!target) {
|
||||
const baseUrl =
|
||||
type === 'external' ? this.externalBaseUrl : this.internalBaseUrl;
|
||||
|
||||
return `${baseUrl}/${encodeURIComponent(pluginId)}`;
|
||||
}
|
||||
|
||||
if (typeof target === 'string') {
|
||||
return target.replace(
|
||||
/\{\{\s*pluginId\s*\}\}/g,
|
||||
encodeURIComponent(pluginId),
|
||||
);
|
||||
}
|
||||
|
||||
return target[type].replace(
|
||||
/\{\{\s*pluginId\s*\}\}/g,
|
||||
encodeURIComponent(pluginId),
|
||||
);
|
||||
}
|
||||
|
||||
async getBaseUrl(pluginId: string): Promise<string> {
|
||||
return this.getTargetFromConfig(pluginId, 'internal');
|
||||
}
|
||||
|
||||
async getExternalBaseUrl(pluginId: string): Promise<string> {
|
||||
return this.getTargetFromConfig(pluginId, 'external');
|
||||
}
|
||||
}
|
||||
export const HostDiscovery = _HostDiscovery;
|
||||
|
||||
/**
|
||||
* SingleHostDiscovery is a basic PluginEndpointDiscovery implementation
|
||||
@@ -142,4 +41,4 @@ export class HostDiscovery implements PluginEndpointDiscovery {
|
||||
* @public
|
||||
* @deprecated Use {@link HostDiscovery} instead
|
||||
*/
|
||||
export const SingleHostDiscovery = HostDiscovery;
|
||||
export const SingleHostDiscovery = _HostDiscovery;
|
||||
|
||||
@@ -13,5 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { HostDiscovery, SingleHostDiscovery } from './HostDiscovery';
|
||||
export type { PluginEndpointDiscovery } from './types';
|
||||
export {
|
||||
HostDiscovery,
|
||||
SingleHostDiscovery,
|
||||
type PluginEndpointDiscovery,
|
||||
} from './HostDiscovery';
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export type { DiscoveryService as PluginEndpointDiscovery } from '@backstage/backend-plugin-api';
|
||||
@@ -20,9 +20,9 @@ import {
|
||||
MiddlewareFactory,
|
||||
createHttpServer,
|
||||
ExtendedHttpServer,
|
||||
HostDiscovery,
|
||||
DefaultRootHttpRouter,
|
||||
} from '@backstage/backend-app-api';
|
||||
import { HostDiscovery } from '@backstage/backend-common';
|
||||
import {
|
||||
createServiceFactory,
|
||||
BackendFeature,
|
||||
|
||||
Reference in New Issue
Block a user