Add config hot reloading to proxy-backend

When working with the proxy configuration or plugins facilitating the proxy
package the workflow is currently somewhat cumbersom as changes to the proxy
configuration requires a full restart of the backend server.

Use cases where the proxy configuration is updated is when testing out new
routes for plugins to use and updating header configuration like authorization
tokens.

This change updates the proxy-backend to subscribe to configuration changes and
update the router when changes to the proxy is made.

Signed-off-by: Crevil <bjoern.soerensen@gmail.com>
This commit is contained in:
Crevil
2022-07-02 09:24:33 +02:00
parent 31499b9c29
commit a4fa1ce090
3 changed files with 81 additions and 3 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-proxy-backend': minor
---
The proxy-backend now automatically reloads configuration when app-config.yaml is updated.
@@ -57,6 +57,55 @@ describe('createRouter', () => {
});
expect(router).toBeDefined();
});
it('should be able to observe the config', async () => {
const logger = getVoidLogger();
// Grab the subscriber function and use mutable config data to mock a config file change
let subscriber: () => void;
const mutableConfigData: any = {
backend: {
baseUrl: 'https://example.com:7007',
listen: {
port: 7007,
},
},
proxy: {
'/test': {
target: 'https://example.com',
headers: {
Authorization: 'Bearer supersecret',
},
},
},
};
const mockConfig = Object.assign(new ConfigReader(mutableConfigData), {
subscribe: (s: () => void) => {
subscriber = s;
return { unsubscribe: () => {} };
},
});
const discovery = SingleHostDiscovery.fromConfig(mockConfig);
const router = await createRouter({
config: mockConfig,
logger,
discovery,
});
expect(router).toBeDefined();
expect(router.stack[0].regexp).toEqual(/^\/test\/?(?=\/|$)/i);
expect(router.stack[1]).toBeUndefined();
mutableConfigData.proxy['/test2'] = {
target: 'https://example.com',
};
subscriber!();
expect(router.stack[0].regexp).toEqual(/^\/test\/?(?=\/|$)/i);
expect(router.stack[1].regexp).toEqual(/^\/test2\/?(?=\/|$)/i);
});
});
describe('where buildMiddleware would fail', () => {
+27 -3
View File
@@ -186,8 +186,34 @@ export async function createRouter(
const { pathname: pathPrefix } = new URL(externalUrl);
const proxyConfig = options.config.getOptional('proxy') ?? {};
configureMiddlewares(options, router, pathPrefix, proxyConfig);
Object.entries(proxyConfig).forEach(([route, proxyRouteConfig]) => {
if (options.config.subscribe) {
let currentKey = JSON.stringify(proxyConfig);
options.config.subscribe(() => {
const newProxyConfig = options.config.getOptional('proxy') ?? {};
const newKey = JSON.stringify(newProxyConfig);
if (currentKey !== newKey) {
currentKey = newKey;
router.stack = [];
configureMiddlewares(options, router, pathPrefix, newProxyConfig);
}
});
}
return router;
}
function configureMiddlewares(
options: RouterOptions,
router: express.Router,
pathPrefix: string,
proxyConfig: any,
) {
Object.entries<any>(proxyConfig).forEach(([route, proxyRouteConfig]) => {
try {
router.use(
route,
@@ -201,6 +227,4 @@ export async function createRouter(
}
}
});
return router;
}