Merge pull request #2600 from spotify/rugvip/disco

backend: add service discovery interface and implement for single host deployments
This commit is contained in:
Patrik Oldsberg
2020-09-25 13:32:36 +02:00
committed by GitHub
23 changed files with 261 additions and 48 deletions
+6 -5
View File
@@ -22,13 +22,16 @@ import { Logger } from 'winston';
import { createAuthProviderRouter } from '../providers';
import { Config } from '@backstage/config';
import { DatabaseKeyStore, TokenFactory, createOidcRouter } from '../identity';
import { NotFoundError } from '@backstage/backend-common';
import {
NotFoundError,
PluginEndpointDiscovery,
} from '@backstage/backend-common';
export interface RouterOptions {
logger: Logger;
database: Knex;
config: Config;
basePath?: string;
discovery: PluginEndpointDiscovery;
}
export async function createRouter(
@@ -38,9 +41,7 @@ export async function createRouter(
const logger = options.logger.child({ plugin: 'auth' });
const appUrl = options.config.getString('app.baseUrl');
const backendUrl = options.config.getString('backend.baseUrl');
// TODO(Rugvip): Replace with service discovery of external URL
const authUrl = backendUrl + (options.basePath ?? '/api/auth');
const authUrl = await options.discovery.getExternalBaseUrl('auth');
const keyDurationSeconds = 3600;
@@ -23,6 +23,7 @@ import {
createServiceBuilder,
useHotMemoize,
loadBackendConfig,
SingleHostDiscovery,
} from '@backstage/backend-common';
export interface ServerOptions {
@@ -34,6 +35,7 @@ export async function startStandaloneServer(
): Promise<Server> {
const logger = options.logger.child({ service: 'auth-backend' });
const config = ConfigReader.fromConfigs(await loadBackendConfig());
const discovery = SingleHostDiscovery.fromConfig(config);
const database = useHotMemoize(module, () => {
const knex = Knex({
@@ -52,6 +54,7 @@ export async function startStandaloneServer(
logger,
config,
database,
discovery,
});
const service = createServiceBuilder(module)
@@ -17,16 +17,20 @@
import { createRouter } from './router';
import * as winston from 'winston';
import { ConfigReader } from '@backstage/config';
import { loadBackendConfig } from '@backstage/backend-common';
import {
loadBackendConfig,
SingleHostDiscovery,
} from '@backstage/backend-common';
describe('createRouter', () => {
it('works', async () => {
const logger = winston.createLogger();
const config = ConfigReader.fromConfigs(await loadBackendConfig());
const discovery = SingleHostDiscovery.fromConfig(config);
const router = await createRouter({
config,
logger,
pathPrefix: '/proxy',
discovery,
});
expect(router).toBeDefined();
});
+6 -8
View File
@@ -23,12 +23,12 @@ import createProxyMiddleware, {
} from 'http-proxy-middleware';
import { Logger } from 'winston';
import http from 'http';
import { PluginEndpointDiscovery } from '@backstage/backend-common';
export interface RouterOptions {
logger: Logger;
config: Config;
// The URL path prefix that the router itself is mounted as, commonly "/proxy"
pathPrefix: string;
discovery: PluginEndpointDiscovery;
}
export interface ProxyConfig extends ProxyMiddlewareConfig {
@@ -76,16 +76,14 @@ export async function createRouter(
): Promise<express.Router> {
const router = Router();
const externalUrl = await options.discovery.getExternalBaseUrl('proxy');
const { pathname: pathPrefix } = new URL(externalUrl);
const proxyConfig = options.config.getOptional('proxy') ?? {};
Object.entries(proxyConfig).forEach(([route, proxyRouteConfig]) => {
router.use(
route,
buildMiddleware(
options.pathPrefix,
options.logger,
route,
proxyRouteConfig,
),
buildMiddleware(pathPrefix, options.logger, route, proxyRouteConfig),
);
});
@@ -17,6 +17,7 @@
import {
createServiceBuilder,
loadBackendConfig,
SingleHostDiscovery,
} from '@backstage/backend-common';
import { Server } from 'http';
import { Logger } from 'winston';
@@ -37,10 +38,11 @@ export async function startStandaloneServer(
logger.debug('Creating application...');
const config = ConfigReader.fromConfigs(await loadBackendConfig());
const discovery = SingleHostDiscovery.fromConfig(config);
const router = await createRouter({
config,
logger,
pathPrefix: '/proxy',
discovery,
});
const service = createServiceBuilder(module)
.enableCors({ origin: 'http://localhost:3000' })
+17 -8
View File
@@ -26,7 +26,10 @@ import {
PublisherBase,
LocalPublish,
} from '../techdocs';
import { resolvePackagePath } from '@backstage/backend-common';
import {
PluginEndpointDiscovery,
resolvePackagePath,
} from '@backstage/backend-common';
import { Entity } from '@backstage/catalog-model';
import { DocsBuilder } from './helpers';
@@ -35,6 +38,7 @@ type RouterOptions = {
generators: GeneratorBuilder;
publisher: PublisherBase;
logger: Logger;
discovery: PluginEndpointDiscovery;
database?: Knex; // TODO: Make database required when we're implementing database stuff.
config: Config;
dockerClient: Docker;
@@ -52,20 +56,25 @@ export async function createRouter({
config,
dockerClient,
logger,
discovery,
}: RouterOptions): Promise<express.Router> {
const router = Router();
router.get('/docs/:kind/:namespace/:name/*', async (req, res) => {
const baseUrl = config.getString('backend.baseUrl');
const storageUrl = config.getString('techdocs.storageUrl');
const { kind, namespace, name } = req.params;
const entity = (await (
await fetch(
`${baseUrl}/api/catalog/entities/by-name/${kind}/${namespace}/${name}`,
)
).json()) as Entity;
const catalogUrl = await discovery.getBaseUrl('catalog');
const triple = [kind, namespace, name].map(encodeURIComponent).join('/');
const catalogRes = await fetch(`${catalogUrl}/entities/by-name/${triple}`);
if (!catalogRes.ok) {
catalogRes.body.pipe(res.status(catalogRes.status));
return;
}
const entity: Entity = await catalogRes.json();
const docsBuilder = new DocsBuilder({
preparers,
@@ -80,7 +89,7 @@ export async function createRouter({
await docsBuilder.build();
}
return res.redirect(`${storageUrl}${req.path.replace('/docs', '')}`);
res.redirect(`${storageUrl}${req.path.replace('/docs', '')}`);
});
if (publisher instanceof LocalPublish) {
@@ -14,7 +14,10 @@
* limitations under the License.
*/
import { createServiceBuilder } from '@backstage/backend-common';
import {
createServiceBuilder,
SingleHostDiscovery,
} from '@backstage/backend-common';
import { Server } from 'http';
import { Logger } from 'winston';
import { createRouter } from './router';
@@ -39,6 +42,7 @@ export async function startStandaloneServer(
): Promise<Server> {
const logger = options.logger.child({ service: 'techdocs-backend' });
const config = ConfigReader.fromConfigs([]);
const discovery = SingleHostDiscovery.fromConfig(config);
logger.debug('Creating application...');
const preparers = new Preparers();
@@ -61,6 +65,7 @@ export async function startStandaloneServer(
publisher,
dockerClient,
config,
discovery,
});
const service = createServiceBuilder(module)
.enableCors({ origin: 'http://localhost:3000' })