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:
@@ -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', () => {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user