Skip to content

Commit 06d4c0d

Browse files
authored
VM actions should use cluster-proxy Service instead of Route (stolostron#4198)
* VM actions should use cluster-proxy Service instead of Route Signed-off-by: zlayne <zlayne@redhat.com> * Update setup to set cluster proxy route in local dev Signed-off-by: zlayne <zlayne@redhat.com> * update ca cert Signed-off-by: zlayne <zlayne@redhat.com> * better error message handling Signed-off-by: zlayne <zlayne@redhat.com> * log response for testing in cluster Signed-off-by: zlayne <zlayne@redhat.com> * remove test logging Signed-off-by: zlayne <zlayne@redhat.com> * Fix tests and lint errors Signed-off-by: zlayne <zlayne@redhat.com> --------- Signed-off-by: zlayne <zlayne@redhat.com>
1 parent a291723 commit 06d4c0d

File tree

4 files changed

+22
-150
lines changed

4 files changed

+22
-150
lines changed

backend/src/lib/json-request.ts

+7-35
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Agent } from 'https'
44
import { HttpsProxyAgent } from 'https-proxy-agent'
55
import { HeadersInit } from 'node-fetch'
66
import { fetchRetry } from './fetch-retry'
7-
import { getCACertificate } from './serviceAccountToken'
7+
import { getServiceCACertificate } from './serviceAccountToken'
88

99
const { HTTP2_HEADER_CONTENT_TYPE, HTTP2_HEADER_AUTHORIZATION, HTTP2_HEADER_ACCEPT, HTTP2_HEADER_USER_AGENT } =
1010
constants
@@ -64,47 +64,19 @@ export function jsonPost<T = unknown>(
6464
}
6565

6666
export function jsonPut(url: string, body: unknown, token?: string): Promise<PutResponse> {
67-
const headers: HeadersInit = {
68-
[HTTP2_HEADER_CONTENT_TYPE]: 'application/json',
69-
}
67+
const headers: HeadersInit = {}
7068
if (token) headers[HTTP2_HEADER_AUTHORIZATION] = `Bearer ${token}`
7169
return fetchRetry(url, {
7270
method: 'PUT',
7371
headers,
74-
agent: new Agent({ ca: getCACertificate() }),
72+
agent: new Agent({ ca: getServiceCACertificate() }),
7573
body: JSON.stringify(body),
7674
compress: true,
7775
})
78-
.then(async (response) => {
79-
let responseData = undefined
80-
if (response.headers.get('content-type')?.includes('text/plain')) {
81-
try {
82-
responseData = await response.text()
83-
return {
84-
statusCode: response.status,
85-
body: responseData,
86-
}
87-
} catch (err) {
88-
return {
89-
statusCode: response.status,
90-
body: 'Error getting resource text response.',
91-
}
92-
}
93-
} else {
94-
try {
95-
responseData = (await response.json()) as unknown
96-
return {
97-
statusCode: response.status,
98-
body: responseData,
99-
}
100-
} catch (err) {
101-
return {
102-
statusCode: response.status,
103-
body: 'Error getting resource json response.',
104-
}
105-
}
106-
}
107-
})
76+
.then((response) => ({
77+
// No response body from cluster-proxy kubevirt requests.
78+
statusCode: response.status,
79+
}))
10880
.catch((err: Error) => {
10981
const errResult = {
11082
body: {

backend/src/routes/virtualMachineProxy.ts

+5-18
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
import { constants, Http2ServerRequest, Http2ServerResponse, OutgoingHttpHeaders } from 'http2'
33
import { jsonPut, jsonRequest } from '../lib/json-request'
44
import { logger } from '../lib/logger'
5+
import { getMultiClusterEngine } from '../lib/multi-cluster-engine'
56
import { respond, respondInternalServerError } from '../lib/respond'
67
import { getServiceAccountToken } from '../lib/serviceAccountToken'
78
import { getAuthenticatedToken } from '../lib/token'
89
import { ResourceList } from '../resources/resource-list'
9-
import { Route } from '../resources/route'
1010
import { Secret } from '../resources/secret'
1111
import { canAccess } from './events'
1212

@@ -63,32 +63,19 @@ export async function virtualMachineProxy(req: Http2ServerRequest, res: Http2Ser
6363
return undefined
6464
})
6565

66-
// Get cluster proxy host
67-
const proxyServer = await jsonRequest(
68-
process.env.CLUSTER_API_URL +
69-
'/apis/route.openshift.io/v1/namespaces/multicluster-engine/routes/cluster-proxy-addon-user',
70-
token
71-
)
72-
.then((response: Route) => {
73-
const scheme = response?.spec?.tls?.termination ? 'https' : 'http'
74-
return response?.spec?.host ? `${scheme}://${response.spec.host}` : ''
75-
})
76-
.catch((err: Error): undefined => {
77-
logger.error({ msg: 'Error getting cluster proxy Route', error: err.message })
78-
return undefined
79-
})
80-
8166
// req.url is one of: /virtualmachines/<action> OR /virtualmachineinstances/<action>
8267
// the VM name is needed between the kind and action for the correct api url.
8368
const splitURL = req.url.split('/')
8469
const joinedURL = `${splitURL[1]}/${body.vmName}/${splitURL[2]}`
85-
const path = `${proxyServer}/${body.managedCluster}/apis/subresources.kubevirt.io/v1/namespaces/${body.vmNamespace}/${joinedURL}`
70+
const mce = await getMultiClusterEngine()
71+
const proxyService = `https://cluster-proxy-addon-user.${mce?.spec?.targetNamespace || 'multicluster-engine'}.svc.cluster.local:9092`
72+
const proxyURL = process.env.CLUSTER_PROXY_ADDON_USER_ROUTE || proxyService
73+
const path = `${proxyURL}/${body.managedCluster}/apis/subresources.kubevirt.io/v1/namespaces/${body.vmNamespace}/${joinedURL}`
8674
const headers: OutgoingHttpHeaders = { authorization: `Bearer ${managedClusterToken}` }
8775
for (const header of proxyHeaders) {
8876
if (req.headers[header]) headers[header] = req.headers[header]
8977
}
9078

91-
if (!path) return respondInternalServerError(req, res)
9279
await jsonPut(path, {}, managedClusterToken)
9380
.then((results) => {
9481
if (results?.statusCode >= 200 && results?.statusCode < 300) {

backend/test/routes/virtualMachineProxy.test.ts

+7-97
Original file line numberDiff line numberDiff line change
@@ -43,33 +43,7 @@ describe('Virtual Machine actions', function () {
4343
},
4444
],
4545
})
46-
nock(process.env.CLUSTER_API_URL)
47-
.get('/apis/route.openshift.io/v1/namespaces/multicluster-engine/routes/cluster-proxy-addon-user')
48-
.reply(200, {
49-
kind: 'Route',
50-
apiVersion: 'route.openshift.io/v1',
51-
metadata: {
52-
name: 'cluster-proxy-addon-user',
53-
namespace: 'multicluster-engine',
54-
},
55-
spec: {
56-
host: 'testCluster.red-chesterfield.com',
57-
to: {
58-
kind: 'Service',
59-
name: 'cluster-proxy-addon-user',
60-
weight: 100,
61-
},
62-
port: {
63-
targetPort: 'user-port',
64-
},
65-
tls: {
66-
termination: 'reencrypt',
67-
insecureEdgeTerminationPolicy: 'Redirect',
68-
},
69-
wildcardPolicy: 'None',
70-
},
71-
})
72-
nock('https://testcluster.red-chesterfield.com')
46+
nock('https://cluster-proxy-addon-user.multicluster-engine.svc.cluster.local:9092')
7347
.put('/testCluster/apis/subresources.kubevirt.io/v1/namespaces/vmNamespace/virtualmachines/vmName/start')
7448
.reply(200, {
7549
statusCode: 200,
@@ -116,50 +90,15 @@ describe('Virtual Machine actions', function () {
11690
},
11791
],
11892
})
119-
nock(process.env.CLUSTER_API_URL)
120-
.get('/apis/route.openshift.io/v1/namespaces/multicluster-engine/routes/cluster-proxy-addon-user')
121-
.reply(200, {
122-
kind: 'Route',
123-
apiVersion: 'route.openshift.io/v1',
124-
metadata: {
125-
name: 'cluster-proxy-addon-user',
126-
namespace: 'multicluster-engine',
127-
},
128-
spec: {
129-
host: 'testCluster.red-chesterfield.com',
130-
to: {
131-
kind: 'Service',
132-
name: 'cluster-proxy-addon-user',
133-
weight: 100,
134-
},
135-
port: {
136-
targetPort: 'user-port',
137-
},
138-
tls: {
139-
termination: 'reencrypt',
140-
insecureEdgeTerminationPolicy: 'Redirect',
141-
},
142-
wildcardPolicy: 'None',
143-
},
144-
})
145-
nock('https://testcluster.red-chesterfield.com')
93+
nock('https://cluster-proxy-addon-user.multicluster-engine.svc.cluster.local:9092')
14694
.put('/testCluster/apis/subresources.kubevirt.io/v1/namespaces/vmNamespace/virtualmachines/vmName/start')
147-
.reply(500, {
148-
name: 'fetchError',
149-
message: 'error testing...',
150-
})
95+
.reply(500)
15196
const res = await request('PUT', '/virtualmachines/start', {
15297
managedCluster: 'testCluster',
15398
vmName: 'vmName',
15499
vmNamespace: 'vmNamespace',
155100
})
156101
expect(res.statusCode).toEqual(500)
157-
expect(JSON.stringify(await parsePipedJsonBody(res))).toEqual(
158-
JSON.stringify({
159-
name: 'fetchError',
160-
message: 'error testing...',
161-
})
162-
)
163102
})
164103

165104
it('should fail with invalid route and secret', async function () {
@@ -180,10 +119,7 @@ describe('Virtual Machine actions', function () {
180119
kind: 'SecretList',
181120
items: [],
182121
})
183-
nock(process.env.CLUSTER_API_URL)
184-
.get('/apis/route.openshift.io/v1/namespaces/multicluster-engine/routes/cluster-proxy-addon-user')
185-
.reply(400)
186-
nock('https://testcluster.red-chesterfield.com')
122+
nock('https://cluster-proxy-addon-user.multicluster-engine.svc.cluster.local:9092')
187123
.put('/testCluster/apis/subresources.kubevirt.io/v1/namespaces/vmNamespace/virtualmachines/vmName/start')
188124
.reply(500, {
189125
name: 'Error',
@@ -231,41 +167,15 @@ describe('Virtual Machine actions', function () {
231167
},
232168
],
233169
})
234-
nock(process.env.CLUSTER_API_URL)
235-
.get('/apis/route.openshift.io/v1/namespaces/multicluster-engine/routes/cluster-proxy-addon-user')
236-
.reply(200, {
237-
kind: 'Route',
238-
apiVersion: 'route.openshift.io/v1',
239-
metadata: {
240-
name: 'cluster-proxy-addon-user',
241-
namespace: 'multicluster-engine',
242-
},
243-
spec: {
244-
host: 'testCluster.red-chesterfield.com',
245-
to: {
246-
kind: 'Service',
247-
name: 'cluster-proxy-addon-user',
248-
weight: 100,
249-
},
250-
port: {
251-
targetPort: 'user-port',
252-
},
253-
tls: {
254-
termination: 'reencrypt',
255-
insecureEdgeTerminationPolicy: 'Redirect',
256-
},
257-
wildcardPolicy: 'None',
258-
},
259-
})
260-
nock('https://testcluster.red-chesterfield.com')
170+
nock('https://cluster-proxy-addon-user.multicluster-engine.svc.cluster.local:9092')
261171
.put('/testCluster/apis/subresources.kubevirt.io/v1/namespaces/vmNamespace/virtualmachines/vmName/start')
262-
.reply(502, 'plain text error', { [constants.HTTP2_HEADER_CONTENT_TYPE]: ['text/plain'] })
172+
.reply(502, '', { [constants.HTTP2_HEADER_CONTENT_TYPE]: ['text/plain'] })
263173
const res = await request('PUT', '/virtualmachines/start', {
264174
managedCluster: 'testCluster',
265175
vmName: 'vmName',
266176
vmNamespace: 'vmNamespace',
267177
})
268178
expect(res.statusCode).toEqual(502)
269-
expect(await parsePipedJsonBody(res)).toEqual('plain text error')
179+
expect(await parsePipedJsonBody(res)).toEqual('')
270180
})
271181
})

setup.sh

+3
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,6 @@ EOF
101101
SEARCH_API_URL=https://$(oc get route search-api -n $INSTALLATION_NAMESPACE -o="jsonpath={.status.ingress[0].host}")
102102
echo SEARCH_API_URL=$SEARCH_API_URL >> ./backend/.env
103103
fi
104+
105+
CLUSTER_PROXY_ADDON_USER_ROUTE=https://$(oc get route cluster-proxy-addon-user -n $INSTALLATION_NAMESPACE_MCE -o="jsonpath={.status.ingress[0].host}")
106+
echo CLUSTER_PROXY_ADDON_USER_ROUTE=$CLUSTER_PROXY_ADDON_USER_ROUTE >> ./backend/.env

0 commit comments

Comments
 (0)