Skip to content

Commit ba4599b

Browse files
Randy424claude
andcommitted
ACM-38826 fix(backend): use GET /api with body drain instead of HEAD
HEAD /api returns 405 on clusters where the API server or proxy chain rejects HEAD requests, causing every auth check to fail. Switch to GET /api with explicit body drain — the response is ~200 bytes (core API group only), drained immediately so the socket returns to the keepAlive pool. This preserves the memory optimization while restoring compatibility. Signed-off-by: Randy Bruno Piverger <21374229+Randy424@users.noreply.github.com> Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent dbabbc9 commit ba4599b

13 files changed

Lines changed: 39 additions & 40 deletions

backend/src/lib/token.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,14 @@ export function getToken(req: Http2ServerRequest): string | undefined {
2828
return token
2929
}
3030

31-
// HEAD /api returns headers only — no response body — so no drain is needed and
32-
// the payload is ~200 bytes regardless of how many CRDs are registered.
33-
// Returns the HTTP status so callers can distinguish 401 (invalid token) from
34-
// 403 (valid token, insufficient permission) and 5xx (transient upstream error).
31+
// GET /api returns the core API group (~200 bytes) — unlike /apis which grows
32+
// with every installed CRD. The response body is drained so the socket returns
33+
// to the keepAlive pool immediately and native memory does not accumulate.
3534
export async function isAuthenticated(token: string): Promise<number> {
3635
const response = await fetchRetry(process.env.CLUSTER_API_URL + '/api', {
37-
method: 'HEAD',
3836
headers: { [HTTP2_HEADER_AUTHORIZATION]: `Bearer ${token}` },
3937
})
38+
response.body?.on('error', () => undefined).resume()
4039
return response.status
4140
}
4241

backend/test/routes/aggregator.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import type { IResource } from '../../src/resources/resource'
1717
describe(`aggregator Route`, function () {
1818
it(`should page Unfiltered Applications`, async function () {
1919
resetApplicationCache()
20-
nock(process.env.CLUSTER_API_URL).head('/api').reply(200)
20+
nock(process.env.CLUSTER_API_URL).get('/api').reply(200)
2121

2222
// initialize events
2323
await Promise.all(resources.map((resource) => cacheResource(resource)))
@@ -63,7 +63,7 @@ describe(`aggregator Route`, function () {
6363
})
6464
it(`should page Filtered Applications`, async function () {
6565
resetApplicationCache()
66-
nock(process.env.CLUSTER_API_URL).head('/api').reply(200)
66+
nock(process.env.CLUSTER_API_URL).get('/api').reply(200)
6767

6868
// initialize events
6969
await Promise.all(resources.map((resource) => cacheResource(resource)))
@@ -93,7 +93,7 @@ describe(`aggregator Route`, function () {
9393
})
9494
it(`should return application counts`, async function () {
9595
resetApplicationCache()
96-
nock(process.env.CLUSTER_API_URL).head('/api').reply(200)
96+
nock(process.env.CLUSTER_API_URL).get('/api').reply(200)
9797

9898
// initialize events
9999
await Promise.all(resources.map((resource) => cacheResource(resource)))
@@ -116,7 +116,7 @@ describe(`aggregator Route`, function () {
116116
})
117117
it(`should return appset data`, async function () {
118118
resetApplicationCache()
119-
nock(process.env.CLUSTER_API_URL).head('/api').reply(200)
119+
nock(process.env.CLUSTER_API_URL).get('/api').reply(200)
120120

121121
// initialize events
122122
resources.forEach((resource) => cacheResource(resource))

backend/test/routes/ansibletower.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const TOWER_HOST = 'https://ansible-tower.com'
88

99
describe(`ansibletower Route`, function () {
1010
it(`should list Ansible Automation controller Jobs`, async function () {
11-
nock(process.env.CLUSTER_API_URL).head('/api').reply(200)
11+
nock(process.env.CLUSTER_API_URL).get('/api').reply(200)
1212
nock(TOWER_HOST).get(ansiblePaths[0]).reply(200, response)
1313
const res = await request('POST', '/ansibletower', {
1414
towerHost: TOWER_HOST + ansiblePaths[0],
@@ -19,7 +19,7 @@ describe(`ansibletower Route`, function () {
1919
})
2020

2121
it(`when bad things happen to Ansible Automation controller Jobs 1`, async function () {
22-
nock(process.env.CLUSTER_API_URL).head('/api').reply(200)
22+
nock(process.env.CLUSTER_API_URL).get('/api').reply(200)
2323
nock(TOWER_HOST).get(ansiblePaths[0]).reply(200, response)
2424
const res = await request('POST', '/ansibletower', {
2525
towerHost: TOWER_HOST + '/badPath',
@@ -30,7 +30,7 @@ describe(`ansibletower Route`, function () {
3030
})
3131

3232
it(`when bad things happen to Ansible Automation controller Jobs 2`, async function () {
33-
nock(process.env.CLUSTER_API_URL).head('/api').reply(200)
33+
nock(process.env.CLUSTER_API_URL).get('/api').reply(200)
3434
nock(TOWER_HOST).get(ansiblePaths[0]).reply(200, response)
3535
const res = await request('POST', '/ansibletower', {
3636
token: '12345',
@@ -39,7 +39,7 @@ describe(`ansibletower Route`, function () {
3939
})
4040

4141
it(`when bad things happen to Ansible Automation controller Jobs 3`, async function () {
42-
nock(process.env.CLUSTER_API_URL).head('/api').reply(401)
42+
nock(process.env.CLUSTER_API_URL).get('/api').reply(401)
4343
const res = await request('POST', '/ansibletower')
4444
expect(res.statusCode).toEqual(401)
4545
expect(JSON.stringify(await parsePipedJsonBody(res))).toEqual(JSON.stringify({}))

backend/test/routes/apiPath.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ describe(`apiPath Route`, function () {
1515

1616
nock(process.env.CLUSTER_API_URL).get(paths[0]).reply(200, response)
1717

18-
nock(process.env.CLUSTER_API_URL).head('/api').reply(200, {
18+
nock(process.env.CLUSTER_API_URL).get('/api').reply(200, {
1919
status: 200,
2020
paths: response,
2121
})

backend/test/routes/hub.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { request } from '../mock-request'
55

66
describe('global hub', function () {
77
it('should return the boolean', async function () {
8-
nock(process.env.CLUSTER_API_URL).head('/api').reply(200)
8+
nock(process.env.CLUSTER_API_URL).get('/api').reply(200)
99
nock(process.env.CLUSTER_API_URL)
1010
.get('/apis/apiextensions.k8s.io/v1/customresourcedefinitions') // .reply(200, { isGlobalHub: true })
1111
.reply(200, {

backend/test/routes/hypershift-status.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { parseResponseJsonBody } from '../../src/lib/body-parser'
44
import nock from 'nock'
55

66
describe('hypershift-status Route', function () {
7-
const mockAuth = () => nock(process.env.CLUSTER_API_URL).head('/api').reply(200, { status: 200 })
7+
const mockAuth = () => nock(process.env.CLUSTER_API_URL).get('/api').reply(200, { status: 200 })
88

99
const mockMCE = (hypershiftEnabled = true, localHostingEnabled = true) =>
1010
nock(process.env.CLUSTER_API_URL)

backend/test/routes/metricsProxy.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ import { request } from '../mock-request'
44

55
describe('metrics proxy route', function () {
66
it('Successfully calls prometheus endpoint', async function () {
7-
nock(process.env.CLUSTER_API_URL).head('/api').reply(200, {
7+
nock(process.env.CLUSTER_API_URL).get('/api').reply(200, {
88
status: 200,
99
})
1010
const res = await request('GET', '/prometheus/query')
1111
expect(res.statusCode).toEqual(200)
1212
})
1313
it(`Successfully calls observability endpoint`, async function () {
14-
nock(process.env.CLUSTER_API_URL).head('/api').reply(200, {
14+
nock(process.env.CLUSTER_API_URL).get('/api').reply(200, {
1515
status: 200,
1616
})
1717
const res = await request('GET', '/observability/query')

backend/test/routes/operatorCheck.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const subscriptionOperators = {
2323

2424
describe(`operatorCheck Route`, function () {
2525
it(`returns valid response with version for installed operator`, async function () {
26-
nock(process.env.CLUSTER_API_URL).head('/api').reply(200, {
26+
nock(process.env.CLUSTER_API_URL).get('/api').reply(200, {
2727
status: 200,
2828
})
2929
nock(process.env.CLUSTER_API_URL)
@@ -38,7 +38,7 @@ describe(`operatorCheck Route`, function () {
3838
})
3939
})
4040
it(`returns valid response for not-installed operator`, async function () {
41-
nock(process.env.CLUSTER_API_URL).head('/api').reply(200, {
41+
nock(process.env.CLUSTER_API_URL).get('/api').reply(200, {
4242
status: 200,
4343
})
4444
nock(process.env.CLUSTER_API_URL)
@@ -52,7 +52,7 @@ describe(`operatorCheck Route`, function () {
5252
})
5353
})
5454
it(`returns bad request for arbitrary operator`, async function () {
55-
nock(process.env.CLUSTER_API_URL).head('/api').reply(200, {
55+
nock(process.env.CLUSTER_API_URL).get('/api').reply(200, {
5656
status: 200,
5757
})
5858
nock(process.env.CLUSTER_API_URL)

backend/test/routes/search.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import nock from 'nock'
44

55
describe(`search Route`, function () {
66
it(`uses search-api in the namespace of the MultiClusterHub`, async function () {
7-
nock(process.env.CLUSTER_API_URL).head('/api').reply(200, {
7+
nock(process.env.CLUSTER_API_URL).get('/api').reply(200, {
88
status: 200,
99
})
1010
nock(process.env.CLUSTER_API_URL)
@@ -27,7 +27,7 @@ describe(`search Route`, function () {
2727
//expect(res.statusCode).toEqual(200)
2828
})
2929
it(`uses search-api in namespace of pod if no MultiClusterHub`, async function () {
30-
nock(process.env.CLUSTER_API_URL).head('/api').reply(200, {
30+
nock(process.env.CLUSTER_API_URL).get('/api').reply(200, {
3131
status: 200,
3232
})
3333
nock(process.env.CLUSTER_API_URL).get('/apis/operator.open-cluster-management.io/v1/multiclusterhubs').reply(200, {

backend/test/routes/upgrade-risks-prediction.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { request } from '../mock-request'
55

66
describe('Upgrade risks prediction Route', function () {
77
it('should return the upgrade risks', async function () {
8-
nock(process.env.CLUSTER_API_URL).head('/api').reply(200)
8+
nock(process.env.CLUSTER_API_URL).get('/api').reply(200)
99
nock(process.env.CLUSTER_API_URL)
1010
.get('/api/v1/namespaces/openshift-config/secrets')
1111
.reply(200, {

0 commit comments

Comments
 (0)