Skip to content

Commit 9ba397a

Browse files
committed
refactor
Signed-off-by: Javan Lacerda <javanlacerda@google.com>
1 parent 2114adf commit 9ba397a

8 files changed

Lines changed: 69 additions & 181 deletions

File tree

src/clusterfuzz/_internal/k8s/job_template.yaml

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,44 @@ apiVersion: batch/v1
1515
kind: Job
1616
metadata:
1717
name: "{{job_name}}"
18+
labels:
19+
task_name: "{{task_name}}"
20+
job_name: "{{clusterfuzz_job_name}}"
1821
spec:
1922
ttlSecondsAfterFinished: 100
2023
activeDeadlineSeconds: {{active_deadline_seconds}}
2124
template:
25+
metadata:
26+
labels:
27+
task_name: "{{task_name}}"
28+
job_name: "{{clusterfuzz_job_name}}"
29+
{% if is_kata %}
30+
app.kubernetes.io/name: clusterfuzz-kata-job
31+
{% endif %}
2232
spec:
33+
{% if is_kata %}
34+
runtimeClassName: kata
35+
{% endif %}
36+
dnsPolicy: ClusterFirstWithHostNet
2337
serviceAccountName: "{{service_account_name}}"
2438
containers:
2539
- name: "{{job_name}}"
2640
image: "{{docker_image}}"
2741
imagePullPolicy: IfNotPresent
42+
lifecycle:
43+
postStart:
44+
exec:
45+
command:
46+
- /bin/sh
47+
- -c
48+
- mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix
49+
resources:
50+
requests:
51+
cpu: '2'
52+
memory: 3.75Gi
53+
limits:
54+
cpu: '2'
55+
memory: 3.75Gi
2856
env:
2957
- name: HOST_UID
3058
value: '1337'
@@ -57,5 +85,9 @@ spec:
5785
emptyDir:
5886
medium: Memory
5987
sizeLimit: 1.9Gi
88+
{% if is_kata %}
89+
nodeSelector:
90+
cloud.google.com/gke-nodepool: kata-enabled-pool
91+
{% endif %}
6092
restartPolicy: Never
61-
backoffLimit: 0
93+
backoffLimit: 0

src/clusterfuzz/_internal/k8s/kata_job_template.yaml

Lines changed: 0 additions & 79 deletions
This file was deleted.

src/clusterfuzz/_internal/k8s/service.py

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,7 @@ def _create_job_body(config: KubernetesJobConfig, input_url: str,
136136
service_account_name: str) -> dict:
137137
"""Creates the body of a Kubernetes job."""
138138

139-
job_name = config.job_type.replace('_', '-') + '-' + str(uuid.uuid4()).split(
140-
'-', maxsplit=1)[0]
139+
job_name = f'cf-job-{str(uuid.uuid4())}'
141140
job_name = job_name.lower()
142141

143142
# Set up Jinja2 environment and load the template.
@@ -153,6 +152,9 @@ def _create_job_body(config: KubernetesJobConfig, input_url: str,
153152
'docker_image': config.docker_image,
154153
'clusterfuzz_release': config.clusterfuzz_release,
155154
'input_url': input_url,
155+
'is_kata': config.is_kata,
156+
'task_name': config.command,
157+
'clusterfuzz_job_name': config.job_type,
156158
}
157159

158160
# Render the template and load as YAML.
@@ -314,39 +316,6 @@ def create_utask_main_jobs(self, remote_tasks: typing.List[types.RemoteTask]):
314316
for config, input_urls in job_specs.items():
315317
# TODO(javanlacerda): Batch multiple tasks into a single job.
316318
for input_url in input_urls:
317-
if config.is_kata:
318-
jobs.append(self.create_kata_container_job(config, input_url))
319-
else:
320-
jobs.append(self.create_job(config, input_url))
319+
jobs.append(self.create_job(config, input_url))
321320

322321
return jobs
323-
324-
def create_kata_container_job(self, config: KubernetesJobConfig,
325-
input_url: str) -> str:
326-
"""Creates a Kubernetes job that runs in a Kata container."""
327-
service_account_name = self._create_service_account_if_needed(
328-
config.service_account_email)
329-
job_name = 'clusterfuzz-kata-job-' + str(uuid.uuid4()).split(
330-
'-', maxsplit=1)[0]
331-
332-
# Set up Jinja2 environment and load the template.
333-
template_dir = os.path.dirname(__file__)
334-
jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir))
335-
template = jinja_env.get_template('kata_job_template.yaml')
336-
337-
# Define the context with all the dynamic values.
338-
context = {
339-
'job_name': job_name,
340-
'active_deadline_seconds': tasks.get_task_duration(config.command),
341-
'service_account_name': service_account_name,
342-
'docker_image': config.docker_image,
343-
'clusterfuzz_release': config.clusterfuzz_release,
344-
'input_url': input_url,
345-
}
346-
347-
# Render the template and load as YAML.
348-
rendered_spec = template.render(context)
349-
job_spec = yaml.safe_load(rendered_spec)
350-
351-
self._batch_api.create_namespaced_job(body=job_spec, namespace='default')
352-
return job_name

src/clusterfuzz/_internal/tests/core/k8s/k8s_service_e2e_test.py

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -220,40 +220,6 @@ def test_create_job(self, mock_get_logging_config_dict):
220220
namespace='default',
221221
body=k8s_client.V1DeleteOptions(propagation_policy='Foreground'))
222222

223-
@unittest.skip('Should be implemented against a cluster that supports kata')
224-
def test_create_kata_container_job(self, mock_get_logging_config_dict):
225-
"""Tests creating a Kata container job."""
226-
input_urls = []
227-
actual_job_name = self.kubernetes_client.create_kata_container_job(
228-
self.image, input_urls)
229-
230-
# Wait for the job to be created.
231-
time.sleep(5)
232-
233-
job = self.api_client.read_namespaced_job(actual_job_name, 'default')
234-
self.assertIsNotNone(job)
235-
self.assertEqual(job.metadata.name, actual_job_name)
236-
self.assertEqual(job.spec.template.spec.runtime_class_name, 'kata')
237-
238-
# Wait for the job to start running.
239-
job_running = False
240-
for _ in range(180):
241-
job = self.api_client.read_namespaced_job(actual_job_name, 'default')
242-
if job.status.active or job.status.succeeded:
243-
job_running = True
244-
break
245-
time.sleep(1)
246-
247-
self.assertTrue(
248-
job_running,
249-
f'Kata Job {actual_job_name} did not start running. Status: {job.status}'
250-
)
251-
252-
self.api_client.delete_namespaced_job(
253-
name=actual_job_name,
254-
namespace='default',
255-
body=k8s_client.V1DeleteOptions(propagation_policy='Foreground'))
256-
257223
@mock.patch('clusterfuzz._internal.k8s.service._get_k8s_job_configs')
258224
@mock.patch(
259225
'clusterfuzz._internal.base.tasks.task_utils.get_command_from_module')

src/clusterfuzz/_internal/tests/core/k8s/k8s_service_limit_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def test_create_uworker_main_batch_jobs_limit_not_reached(
5252

5353
# We expect this to proceed to job creation logic (which we mock to avoid actual creation)
5454
with mock.patch.object(service.KubernetesService,
55-
'create_kata_container_job') as mock_create:
55+
'create_job') as mock_create:
5656
kube_service.create_utask_main_jobs(
5757
[types.RemoteTask('fuzz', 'job1', 'url1')])
5858
self.assertTrue(mock_create.called)

src/clusterfuzz/_internal/tests/core/k8s/k8s_service_test.py

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,9 @@ def setUp(self):
4848
value=100.0).put()
4949

5050
@mock.patch.object(service.KubernetesService, '_get_pending_jobs_count')
51-
@mock.patch.object(service.KubernetesService, 'create_kata_container_job')
5251
@mock.patch.object(service.KubernetesService, 'create_job')
5352
def test_create_uworker_main_batch_jobs(
54-
self, mock_create_job, mock_create_kata_job, mock_get_pending_count, _):
53+
self, mock_create_job, mock_get_pending_count, _):
5554
"""Tests the creation of uworker main batch jobs."""
5655
mock_get_pending_count.return_value = 0
5756
tasks = [
@@ -63,13 +62,11 @@ def test_create_uworker_main_batch_jobs(
6362
kube_service = service.KubernetesService()
6463
kube_service.create_utask_main_jobs(tasks)
6564

66-
# Assuming default config implies Kata, and no batching of URLs.
67-
# Total 3 tasks, so 3 calls.
68-
self.assertEqual(3, mock_create_kata_job.call_count)
69-
self.assertEqual(0, mock_create_job.call_count)
65+
# Total 3 tasks, so 3 calls to create_job.
66+
self.assertEqual(3, mock_create_job.call_count)
7067

7168
urls = sorted(
72-
[call.args[1] for call in mock_create_kata_job.call_args_list])
69+
[call.args[1] for call in mock_create_job.call_args_list])
7370
self.assertEqual(urls, ['url1', 'url2', 'url3'])
7471

7572
@mock.patch('kubernetes.client.CoreV1Api')
@@ -106,8 +103,8 @@ def test_create_uworker_main_batch_jobs_limit_reached(
106103
self.assertTrue(mock_pubsub_task.do_not_ack)
107104

108105
@mock.patch('kubernetes.client.BatchV1Api')
109-
def test_create_kata_container_job_spec(self, mock_batch_api_cls, _):
110-
"""Tests that create_kata_container_job generates the correct spec."""
106+
def test_create_job_kata(self, mock_batch_api_cls, _):
107+
"""Tests that create_job generates the correct spec for Kata."""
111108
mock_batch_api = mock_batch_api_cls.return_value
112109
kube_service = service.KubernetesService()
113110
# Mock _create_service_account_if_needed
@@ -123,7 +120,7 @@ def test_create_kata_container_job_spec(self, mock_batch_api_cls, _):
123120
clusterfuzz_release='prod',
124121
is_kata=True)
125122

126-
kube_service.create_kata_container_job(config, 'input_url')
123+
kube_service.create_job(config, 'input_url')
127124

128125
self.assertTrue(mock_batch_api.create_namespaced_job.called)
129126
call_args = mock_batch_api.create_namespaced_job.call_args
@@ -137,22 +134,18 @@ def test_create_kata_container_job_spec(self, mock_batch_api_cls, _):
137134
self.assertEqual(['ALL'],
138135
container['securityContext']['capabilities']['add'])
139136

140-
# Check HOST_UID env var
141-
env_names = {e['name']: e['value'] for e in container['env']}
142-
self.assertEqual('1337', env_names['HOST_UID'])
143-
144-
# Check shm size
145-
volumes = {v['name']: v for v in pod_spec['volumes']}
146-
self.assertEqual('1.9Gi', volumes['dshm']['emptyDir']['sizeLimit'])
137+
# Check labels
138+
self.assertEqual('fuzz', job_body['metadata']['labels']['task_name'])
139+
self.assertEqual('test-job', job_body['metadata']['labels']['job_name'])
147140

148-
# Check Service Account
149-
self.assertEqual('test', pod_spec['serviceAccountName'])
150-
kube_service._create_service_account_if_needed.assert_called_with(
151-
'test@clusterfuzz-test.iam.gserviceaccount.com')
141+
# Check Kata specific fields
142+
self.assertEqual('kata', pod_spec['runtimeClassName'])
143+
self.assertEqual('ClusterFirstWithHostNet', pod_spec['dnsPolicy'])
144+
self.assertIn('lifecycle', container)
152145

153146
@mock.patch('kubernetes.client.BatchV1Api')
154-
def test_create_job(self, mock_batch_api_cls, _):
155-
"""Tests create_job."""
147+
def test_create_job_standard(self, mock_batch_api_cls, _):
148+
"""Tests create_job for standard container."""
156149
mock_batch_api = mock_batch_api_cls.return_value
157150
kube_service = service.KubernetesService()
158151
kube_service._create_service_account_if_needed = mock.Mock(
@@ -173,11 +166,15 @@ def test_create_job(self, mock_batch_api_cls, _):
173166
call_args = mock_batch_api.create_namespaced_job.call_args
174167
job_body = call_args.kwargs['body']
175168

169+
pod_spec = job_body['spec']['template']['spec']
170+
container = pod_spec['containers'][0]
171+
176172
# Check Service Account
177-
self.assertEqual('test-sa',
178-
job_body['spec']['template']['spec']['serviceAccountName'])
179-
kube_service._create_service_account_if_needed.assert_called_with(
180-
'test-email')
173+
self.assertEqual('test-sa', pod_spec['serviceAccountName'])
174+
175+
# Check that Kata specific fields are NOT present
176+
self.assertNotIn('runtimeClassName', pod_spec)
177+
self.assertIn('volumeMounts', container)
181178

182179
@mock.patch(
183180
'clusterfuzz._internal.base.tasks.task_utils.get_command_from_module')
@@ -194,4 +191,4 @@ def test_create_uworker_main_batch_job(self, mock_create_batch_jobs,
194191
self.assertEqual(1, len(tasks))
195192
self.assertEqual('command', tasks[0].command)
196193
self.assertEqual('job', tasks[0].job_type)
197-
self.assertEqual('url', tasks[0].input_download_url)
194+
self.assertEqual('url', tasks[0].input_download_url)

src/clusterfuzz/_internal/tests/core/kubernetes/kubernetes_test.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,10 @@ def test_create_job(self):
7777
called_args, called_kwargs = self.k8s_client._batch_api.create_namespaced_job.call_args
7878
self.assertEqual(called_args, ())
7979
job_body = called_kwargs['body']
80-
self.assertEqual(job_body['metadata']['name'], 'test-job-a0b1c2d3')
80+
self.assertEqual(job_body['metadata']['name'],
81+
'cf-job-a0b1c2d3-e4f5-6789-0123-456789abcdef')
82+
self.assertEqual(job_body['metadata']['labels']['task_name'], 'fuzz')
83+
self.assertEqual(job_body['metadata']['labels']['job_name'], 'test-job')
8184
self.assertEqual(
8285
job_body['spec']['template']['spec']['containers'][0]['image'],
8386
'test-image')

src/local/butler/lint.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
_LICENSE_CHECK_IGNORE = 'LICENSE_CHECK_IGNORE'
5252
_PY_TEST_SUFFIX = '_test.py'
5353
_PY_INIT_FILENAME = '__init__.py'
54-
_YAML_EXCEPTIONS = ['bad.yaml', 'job_template.yaml', 'kata_job_template.yaml']
54+
_YAML_EXCEPTIONS = ['bad.yaml', 'job_template.yaml']
5555

5656
_error_occurred = False
5757

0 commit comments

Comments
 (0)