Skip to content

Commit 5a509a5

Browse files
authored
Merge pull request #36 from safeinsights/shrimp/sk/54
SHRIMP-54: Kubernetes Implementation & Trivy security configuration updates
2 parents 23efe6d + 2345b8f commit 5a509a5

File tree

14 files changed

+845
-201
lines changed

14 files changed

+845
-201
lines changed

src/lib/api.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
import http from 'http'
2+
import https from 'https'
13
import jwt from 'jsonwebtoken'
4+
import { LogEntry } from './aws'
5+
import { getKubeAPIServiceAccountToken, getNamespace, initHTTPSTrustStore } from './kube'
26
import {
37
DockerApiResponse,
48
KubernetesApiResponse,
@@ -7,11 +11,7 @@ import {
711
isManagementAppGetReadyStudiesResponse,
812
isTOAGetJobsResponse,
913
} from './types'
10-
import http from 'http'
11-
import https from 'https'
1214
import { hasReadPermissions } from './utils'
13-
import { getKubeAPIServiceAccountToken, getNamespace, initHTTPSTrustStore } from './kube'
14-
import { LogEntry } from './aws'
1515

1616
// Functions for interacting with the Management App
1717
const generateManagementAppToken = (): string => {
@@ -245,14 +245,15 @@ export const dockerApiCall = async (
245245
}
246246

247247
export const k8sApiCall = (
248-
group: string,
248+
group: string | undefined,
249249
path: string,
250250
method: string,
251251
body?: unknown,
252252
): Promise<KubernetesApiResponse> => {
253253
const namespace = getNamespace()
254254
const kubeAPIServer = process.env.K8S_APISERVER || `https://kubernetes.default.svc.cluster.local`
255-
const kubeAPIServerURL = `${kubeAPIServer}/apis/${group}/v1/namespaces/${namespace}/${path}`
255+
const apiPrefix = group === undefined ? 'api' : `apis/${group}`
256+
const kubeAPIServerURL = `${kubeAPIServer}/${apiPrefix}/v1/namespaces/${namespace}/${path}`
256257
const kubeAPIServerAccountToken = getKubeAPIServiceAccountToken()
257258
initHTTPSTrustStore()
258259
console.log(`K8s: Making ${method} => ${kubeAPIServerURL}`)

src/lib/check-jobs.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ describe('checkForErroredJobs', () => {
2222
it('should call kubernetes error check when deployment environment is Kubernetes', async () => {
2323
process.env.DEPLOYMENT_ENVIRONMENT = 'KUBERNETES'
2424
const checkForKubernetesErroredJobsMock = vi.spyOn(KubernetesEnclave.prototype, 'checkForErroredJobs')
25-
await expect(checkForErroredJobs()).rejects.toThrow('Method not implemented.')
25+
await expect(checkForErroredJobs()).rejects.toThrow('Namespace file not found!')
2626
expect(checkForKubernetesErroredJobsMock).toHaveBeenCalled()
2727
})
2828
it('should throw an error when deployment environment is unsupported', async () => {

src/lib/docker-enclave.test.ts

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as api from './api'
33
import * as docker from './docker'
44
import { DockerEnclave } from './docker-enclave'
55
import {
6+
CONTAINER_TYPES,
67
DockerApiContainerResponse,
78
DockerApiContainersResponse,
89
ManagementAppGetReadyStudiesResponse,
@@ -49,8 +50,8 @@ describe('DockerEnclave', () => {
4950
Command: 'test/container-1',
5051
Labels: {
5152
instance: '1234567890',
52-
component: 'research-container',
53-
'managed-by': 'setup-app',
53+
component: CONTAINER_TYPES.RESEARCH_CONTAINER,
54+
'managed-by': CONTAINER_TYPES.SETUP_APP,
5455
},
5556
State: 'running',
5657
Status: 'running',
@@ -63,8 +64,8 @@ describe('DockerEnclave', () => {
6364
Command: 'test/container-2',
6465
Labels: {
6566
instance: '0987654321',
66-
component: 'research-container',
67-
'managed-by': 'setup-app',
67+
component: CONTAINER_TYPES.RESEARCH_CONTAINER,
68+
'managed-by': CONTAINER_TYPES.SETUP_APP,
6869
},
6970
State: 'running',
7071
Status: 'running',
@@ -102,8 +103,8 @@ describe('DockerEnclave', () => {
102103
Command: 'test/container-1',
103104
Labels: {
104105
instance: '1234567890',
105-
component: 'research-container',
106-
'managed-by': 'setup-app',
106+
component: CONTAINER_TYPES.RESEARCH_CONTAINER,
107+
'managed-by': CONTAINER_TYPES.SETUP_APP,
107108
},
108109
State: 'completed',
109110
Status: 'completed',
@@ -116,8 +117,8 @@ describe('DockerEnclave', () => {
116117
Command: 'test/container-2',
117118
Labels: {
118119
instance: '0987654321',
119-
component: 'research-container',
120-
'managed-by': 'setup-app',
120+
component: CONTAINER_TYPES.RESEARCH_CONTAINER,
121+
'managed-by': CONTAINER_TYPES.SETUP_APP,
121122
},
122123
State: 'completed',
123124
Status: 'completed',
@@ -163,13 +164,15 @@ describe('DockerEnclave', () => {
163164
.mockResolvedValueOnce(statuses[0])
164165
.mockResolvedValueOnce(statuses[1])
165166
vi.spyOn(enclave, 'getAllStudiesInEnclave').mockResolvedValue(jobs)
167+
const removeContainerSpy = vi.spyOn(enclave, 'removeContainer').mockImplementation(vi.fn())
166168
vi.mocked(docker.filterContainers).mockReturnValue(jobs)
167169
await enclave.cleanup()
168-
expect(dockerApiCall).toHaveBeenCalledTimes(4)
169-
expect(dockerApiCall).toHaveBeenCalledWith('GET', 'containers/0987654321/json')
170+
expect(dockerApiCall).toHaveBeenCalledTimes(2)
170171
expect(dockerApiCall).toHaveBeenCalledWith('GET', 'containers/1234567890/json')
171-
expect(dockerApiCall).toHaveBeenCalledWith('DELETE', 'containers/1234567890')
172-
expect(dockerApiCall).toHaveBeenCalledWith('DELETE', 'containers/0987654321')
172+
expect(dockerApiCall).toHaveBeenCalledWith('GET', 'containers/0987654321/json')
173+
expect(removeContainerSpy).toHaveBeenCalledTimes(2)
174+
expect(removeContainerSpy).toBeCalledWith('1234567890')
175+
expect(removeContainerSpy).toBeCalledWith('0987654321')
173176
})
174177

175178
it('removeContainer: should call dockerApiCall with the correct arguments', async () => {
@@ -207,8 +210,8 @@ describe('DockerEnclave', () => {
207210
Command: 'test/container-1',
208211
Labels: {
209212
instance: '1234567890',
210-
component: 'research-container',
211-
'managed-by': 'setup-app',
213+
component: CONTAINER_TYPES.RESEARCH_CONTAINER,
214+
'managed-by': CONTAINER_TYPES.SETUP_APP,
212215
},
213216
State: 'completed',
214217
Status: 'completed',
@@ -221,8 +224,8 @@ describe('DockerEnclave', () => {
221224
Command: 'test/container-2',
222225
Labels: {
223226
instance: '0987654321',
224-
component: 'research-container',
225-
'managed-by': 'setup-app',
227+
component: CONTAINER_TYPES.RESEARCH_CONTAINER,
228+
'managed-by': CONTAINER_TYPES.SETUP_APP,
226229
},
227230
State: 'completed',
228231
Status: 'completed',
@@ -248,8 +251,8 @@ describe('DockerEnclave', () => {
248251
expect(filterContainers).toHaveBeenCalledWith(
249252
[],
250253
{
251-
component: 'research-container',
252-
'managed-by': 'setup-app',
254+
component: CONTAINER_TYPES.RESEARCH_CONTAINER,
255+
'managed-by': CONTAINER_TYPES.SETUP_APP,
253256
},
254257
['running', 'created', 'restarting', 'removing', 'paused', 'exited', 'dead'],
255258
)
@@ -264,8 +267,8 @@ describe('DockerEnclave', () => {
264267
Command: 'test/container-1',
265268
Labels: {
266269
instance: '1234567890',
267-
component: 'research-container',
268-
'managed-by': 'setup-app',
270+
component: CONTAINER_TYPES.RESEARCH_CONTAINER,
271+
'managed-by': CONTAINER_TYPES.SETUP_APP,
269272
},
270273
State: 'running',
271274
Status: 'running',
@@ -278,8 +281,8 @@ describe('DockerEnclave', () => {
278281
Command: 'test/container-2',
279282
Labels: {
280283
instance: '0987654321',
281-
component: 'research-container',
282-
'managed-by': 'setup-app',
284+
component: CONTAINER_TYPES.RESEARCH_CONTAINER,
285+
'managed-by': CONTAINER_TYPES.SETUP_APP,
283286
},
284287
State: 'completed',
285288
Status: 'completed',
@@ -296,8 +299,8 @@ describe('DockerEnclave', () => {
296299
expect(filterContainers).toHaveBeenCalledWith(
297300
jobs,
298301
{
299-
component: 'research-container',
300-
'managed-by': 'setup-app',
302+
component: CONTAINER_TYPES.RESEARCH_CONTAINER,
303+
'managed-by': CONTAINER_TYPES.SETUP_APP,
301304
},
302305
['running', 'created', 'restarting', 'removing', 'paused', 'exited', 'dead'],
303306
)
@@ -329,10 +332,10 @@ describe('DockerEnclave', () => {
329332
Image: 'my-image:latest',
330333
Labels: {
331334
app: 'rc-123',
332-
component: 'research-container',
335+
component: CONTAINER_TYPES.RESEARCH_CONTAINER,
333336
'part-of': 'My Study',
334337
instance: '123',
335-
'managed-by': 'setup-app',
338+
'managed-by': CONTAINER_TYPES.SETUP_APP,
336339
},
337340
Env: [`TRUSTED_OUTPUT_ENDPOINT=http://example.com/job/123`],
338341
}
@@ -395,8 +398,8 @@ describe('DockerEnclave', () => {
395398
Command: 'test/container-1',
396399
Labels: {
397400
instance: '1234567890',
398-
component: 'research-container',
399-
'managed-by': 'setup-app',
401+
component: CONTAINER_TYPES.RESEARCH_CONTAINER,
402+
'managed-by': CONTAINER_TYPES.SETUP_APP,
400403
},
401404
State: 'exited',
402405
Status: 'exited',

src/lib/docker-enclave.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { dockerApiCall, toaUpdateJobStatus } from './api'
22
import { createContainerObject, filterContainers, pullContainer } from './docker'
33
import { Enclave, IEnclave } from './enclave'
44
import {
5+
CONTAINER_TYPES,
56
DockerApiContainerResponse,
67
DockerApiContainersResponse,
78
DockerApiResponse,
@@ -18,7 +19,7 @@ class DockerEnclave extends Enclave<DockerApiContainersResponse> implements IEnc
1819
): ManagementAppGetReadyStudiesResponse {
1920
console.log('Filtering Docker jobs')
2021
/* v8 ignore next */
21-
if (!runningJobsInEnclave?.length) return bmaReadysResults || { jobs: [] }
22+
if (!runningJobsInEnclave?.length) return { jobs: bmaReadysResults.jobs }
2223
const jobs: ManagementAppJob[] = bmaReadysResults.jobs.filter((job) =>
2324
runningJobsInEnclave.map((r) => r.Labels?.instance !== job.jobId),
2425
)
@@ -33,12 +34,12 @@ class DockerEnclave extends Enclave<DockerApiContainersResponse> implements IEnc
3334
const successfulContainers = filterContainers(
3435
await this.getAllStudiesInEnclave(),
3536
{
36-
component: 'research-container',
37-
'managed-by': 'setup-app',
37+
component: CONTAINER_TYPES.RESEARCH_CONTAINER,
38+
'managed-by': CONTAINER_TYPES.SETUP_APP,
3839
},
3940
['exited'],
4041
)
41-
successfulContainers.forEach(async (container) => {
42+
for (const container of successfulContainers) {
4243
const containerExitResult = (await dockerApiCall(
4344
'GET',
4445
`containers/${container.Id}/json`,
@@ -51,7 +52,7 @@ class DockerEnclave extends Enclave<DockerApiContainersResponse> implements IEnc
5152
`Container ${container.Id} did not exit successfully. Please check the logs for more details. Will be cleaned up during the error jobs fetch.`,
5253
)
5354
}
54-
})
55+
}
5556
}
5657

5758
async removeContainer(id: string): Promise<void> {
@@ -82,8 +83,8 @@ class DockerEnclave extends Enclave<DockerApiContainersResponse> implements IEnc
8283
const containers = filterContainers(
8384
await this.getAllStudiesInEnclave(),
8485
{
85-
component: 'research-container',
86-
'managed-by': 'setup-app',
86+
component: CONTAINER_TYPES.RESEARCH_CONTAINER,
87+
'managed-by': CONTAINER_TYPES.SETUP_APP,
8788
},
8889
['running', 'created', 'restarting', 'removing', 'paused', 'exited', 'dead'],
8990
)
@@ -115,12 +116,12 @@ class DockerEnclave extends Enclave<DockerApiContainersResponse> implements IEnc
115116
const exitedContainers = filterContainers(
116117
await this.getAllStudiesInEnclave(),
117118
{
118-
component: 'research-container',
119-
'managed-by': 'setup-app',
119+
component: CONTAINER_TYPES.RESEARCH_CONTAINER,
120+
'managed-by': CONTAINER_TYPES.SETUP_APP,
120121
},
121122
['exited'],
122123
)
123-
exitedContainers.forEach(async (container) => {
124+
for (const container of exitedContainers) {
124125
const containerExitResult: DockerApiContainerResponse = (await dockerApiCall(
125126
'GET',
126127
`containers/${container.Id}/json`,
@@ -133,7 +134,7 @@ class DockerEnclave extends Enclave<DockerApiContainersResponse> implements IEnc
133134
message: errorMsg,
134135
})
135136
}
136-
})
137+
}
137138
}
138139
}
139140

src/lib/docker.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { describe, expect, it, vi } from 'vitest'
22
import * as api from './api'
33
import { createContainerObject, filterContainers, pullContainer } from './docker'
4-
import { DockerApiContainersResponse } from './types'
4+
import { CONTAINER_TYPES, DockerApiContainersResponse } from './types'
55
vi.mock('./api')
66

77
describe('docker', () => {
@@ -40,10 +40,10 @@ describe('docker', () => {
4040
Image: imageLocation,
4141
Labels: {
4242
app: `research-container-${jobId}`,
43-
component: 'research-container',
43+
component: CONTAINER_TYPES.RESEARCH_CONTAINER,
4444
'part-of': studyTitle.toLowerCase(),
4545
instance: jobId,
46-
'managed-by': 'setup-app',
46+
'managed-by': CONTAINER_TYPES.SETUP_APP,
4747
},
4848
Env: [
4949
`TRUSTED_OUTPUT_ENDPOINT=${toaEndpointWithJobId}`,

src/lib/docker.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { dockerApiCall } from './api'
2-
import { DockerApiContainersResponse, DockerApiResponse } from './types'
2+
import { CONTAINER_TYPES, DockerApiContainersResponse, DockerApiResponse } from './types'
33
import { hasReadPermissions } from './utils'
44

55
async function pullContainer(imageLocation: string): Promise<DockerApiResponse> {
@@ -22,10 +22,10 @@ function createContainerObject(imageLocation: string, jobId: string, studyTitle:
2222
Image: imageLocation,
2323
Labels: {
2424
app: name,
25-
component: 'research-container',
25+
component: CONTAINER_TYPES.RESEARCH_CONTAINER,
2626
'part-of': studyTitle,
2727
instance: jobId,
28-
'managed-by': 'setup-app',
28+
'managed-by': CONTAINER_TYPES.SETUP_APP,
2929
},
3030
Env: [
3131
`TRUSTED_OUTPUT_ENDPOINT=${toaEndpointWithjobId}`,

0 commit comments

Comments
 (0)