[HTTP] Add build number to headers so that we can identify UIs (#163577)

Close https://github.com/elastic/kibana/issues/162332

### List

- [x] Add E2E test

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Jean-Louis Leysens 2023-08-16 16:05:57 +02:00 committed by GitHub
parent 1efec8e0d0
commit a00c2401e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 46 additions and 1 deletions

View file

@ -29,6 +29,7 @@ describe('Fetch', () => {
const fetchInstance = new Fetch({ const fetchInstance = new Fetch({
basePath: new BasePath(BASE_PATH), basePath: new BasePath(BASE_PATH),
kibanaVersion: 'VERSION', kibanaVersion: 'VERSION',
buildNumber: 1234,
executionContext: executionContextMock, executionContext: executionContextMock,
}); });
afterEach(() => { afterEach(() => {
@ -160,6 +161,7 @@ describe('Fetch', () => {
expect(fetchMock.lastOptions()!.headers).toMatchObject({ expect(fetchMock.lastOptions()!.headers).toMatchObject({
'content-type': 'application/json', 'content-type': 'application/json',
'kbn-version': 'VERSION', 'kbn-version': 'VERSION',
'kbn-build-number': '1234',
'x-elastic-internal-origin': 'Kibana', 'x-elastic-internal-origin': 'Kibana',
myheader: 'foo', myheader: 'foo',
}); });
@ -178,6 +180,19 @@ describe('Fetch', () => {
`"Invalid fetch headers, headers beginning with \\"kbn-\\" are not allowed: [kbn-version]"` `"Invalid fetch headers, headers beginning with \\"kbn-\\" are not allowed: [kbn-version]"`
); );
}); });
it('should not allow overwriting of kbn-build-number header', async () => {
fetchMock.get('*', {});
await expect(
fetchInstance.fetch('/my/path', {
headers: {
myHeader: 'foo',
'kbn-build-number': 4321,
},
})
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Invalid fetch headers, headers beginning with \\"kbn-\\" are not allowed: [kbn-build-number]"`
);
});
it('should not allow overwriting of x-elastic-internal-origin header', async () => { it('should not allow overwriting of x-elastic-internal-origin header', async () => {
fetchMock.get('*', {}); fetchMock.get('*', {});

View file

@ -31,6 +31,7 @@ import { HttpInterceptHaltError } from './http_intercept_halt_error';
interface Params { interface Params {
basePath: IBasePath; basePath: IBasePath;
kibanaVersion: string; kibanaVersion: string;
buildNumber: number;
executionContext: ExecutionContextSetup; executionContext: ExecutionContextSetup;
} }
@ -135,6 +136,7 @@ export class Fetch {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
...options.headers, ...options.headers,
'kbn-version': this.params.kibanaVersion, 'kbn-version': this.params.kibanaVersion,
'kbn-build-number': this.params.buildNumber,
[ELASTIC_HTTP_VERSION_HEADER]: version, [ELASTIC_HTTP_VERSION_HEADER]: version,
[X_ELASTIC_INTERNAL_ORIGIN_REQUEST]: 'Kibana', [X_ELASTIC_INTERNAL_ORIGIN_REQUEST]: 'Kibana',
...(!isEmpty(context) ? new ExecutionContextContainer(context).toHeader() : {}), ...(!isEmpty(context) ? new ExecutionContextContainer(context).toHeader() : {}),

View file

@ -31,13 +31,14 @@ export class HttpService implements CoreService<HttpSetup, HttpStart> {
public setup({ injectedMetadata, fatalErrors, executionContext }: HttpDeps): HttpSetup { public setup({ injectedMetadata, fatalErrors, executionContext }: HttpDeps): HttpSetup {
const kibanaVersion = injectedMetadata.getKibanaVersion(); const kibanaVersion = injectedMetadata.getKibanaVersion();
const buildNumber = injectedMetadata.getKibanaBuildNumber();
const basePath = new BasePath( const basePath = new BasePath(
injectedMetadata.getBasePath(), injectedMetadata.getBasePath(),
injectedMetadata.getServerBasePath(), injectedMetadata.getServerBasePath(),
injectedMetadata.getPublicBaseUrl() injectedMetadata.getPublicBaseUrl()
); );
const fetchService = new Fetch({ basePath, kibanaVersion, executionContext }); const fetchService = new Fetch({ basePath, kibanaVersion, buildNumber, executionContext });
const loadingCount = this.loadingCount.setup({ fatalErrors }); const loadingCount = this.loadingCount.setup({ fatalErrors });
loadingCount.addLoadingCountSource(fetchService.getRequestCount$()); loadingCount.addLoadingCountSource(fetchService.getRequestCount$());

View file

@ -22,6 +22,15 @@ export class CoreHttpPlugin implements Plugin {
return res.ok(); return res.ok();
} }
); );
router.get(
{
path: '/api/core_http/headers',
validate: false,
},
async (ctx, req, res) => {
return res.ok({ body: req.headers });
}
);
} }
public start() {} public start() {}

View file

@ -7,6 +7,7 @@
*/ */
import expect from '@kbn/expect'; import expect from '@kbn/expect';
import SemVer from 'semver';
import { PluginFunctionalProviderContext } from '../../services'; import { PluginFunctionalProviderContext } from '../../services';
export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) { export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) {
@ -29,5 +30,22 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide
const canceledErrorName = await getCancelationErrorName(); const canceledErrorName = await getCancelationErrorName();
expect(canceledErrorName).to.eql('AbortError'); expect(canceledErrorName).to.eql('AbortError');
}); });
it('sets the expected headers', async () => {
const headers = await browser.executeAsync<Record<string, string>>(async (cb) => {
cb(await window._coreProvider.setup.core.http.get('/api/core_http/headers'));
});
expect(headers).to.have.property('kbn-version');
expect(!!SemVer.valid(headers['kbn-version'])).to.be(true);
expect(headers).to.have.property('kbn-build-number');
expect(headers['kbn-build-number']).to.match(/^\d+$/);
expect(headers).to.have.property('x-elastic-internal-origin');
expect(headers['x-elastic-internal-origin']).to.be.a('string');
expect(headers).to.have.property('x-kbn-context');
expect(headers['x-kbn-context']).to.be.a('string');
});
}); });
} }