Skip to content

Commit 7e3b1d3

Browse files
Enable deploying chrome-tests-syncer independently (#4647)
### Motivation Decouple the chrome-tests-syncer code releases from the rest of clusterfuzz, in order to enable it to be deployed independently of the clusterfuzz instances by the chrome fuzzing team . ### Changes * Extended the butler deploy script to produce a dedicated deployment package for "--release=chrome-tests-syncer" and to produce both the prod and tests-syncer packages when releasing to prod. * Changed chrome-tests-syncer Dockerfile/start.sh to pull from this dedicated deployment package. * Moved and edited the tests-syncer code to be executed as a k8s cronjob. * (side effect) Updated unit test to account for deploying the chrome-tests-syncer manifest. ### Tests * Deployed a chrome-tests-syncer release using this PR and produced the dedicated artifact (https://screenshot.googleplex.com/8W88DmYwFKPkSfZ.png). * Built an updated chrome-tests-syncer image that pulls from this artifact (https://screenshot.googleplex.com/8HqEvV7bnBggBPj.png). * Created the chrome-tests-syncer cronjob (along with PR in clusterfuzz-config: 2428107) pulling from this updated image, applied it to the GKE and it ran successfully: ![image](https://github.com/user-attachments/assets/b32441f1-daae-4c12-9476-97583fad3982) * Mocked a deployment with --release=prod (by printing the command instead of actually uploading to gcs): <img width="960" alt="Screenshot 2025-02-05 at 11 48 31" src="https://github.com/user-attachments/assets/bd035374-07d1-453a-9db4-2ed0cd39d92f" /> Bug: b/389048599
1 parent 4288eef commit 7e3b1d3

File tree

9 files changed

+60
-45
lines changed

9 files changed

+60
-45
lines changed

butler.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,10 @@ def main():
286286
parser_package.add_argument(
287287
'-p', '--platform', choices=['linux', 'macos', 'windows', 'all'])
288288
parser_package.add_argument(
289-
'-r', '--release', choices=['prod', 'candidate'], default='prod')
289+
'-r',
290+
'--release',
291+
choices=['prod', 'candidate', 'chrome-tests-syncer'],
292+
default='prod')
290293

291294
parser_deploy = subparsers.add_parser('deploy', help='Deploy to Appengine')
292295
parser_deploy.add_argument(
@@ -303,7 +306,10 @@ def main():
303306
parser_deploy.add_argument(
304307
'--targets', nargs='*', default=['appengine', 'k8s', 'zips'])
305308
parser_deploy.add_argument(
306-
'--release', '-r', choices=['prod', 'candidate'], default='prod')
309+
'--release',
310+
'-r',
311+
choices=['prod', 'candidate', 'chrome-tests-syncer'],
312+
default='prod')
307313

308314
parser_run_server = subparsers.add_parser(
309315
'run_server', help='Run the local Clusterfuzz server.')

docker/chromium/tests-syncer/Dockerfile

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,9 @@
1313
# limitations under the License.
1414
FROM gcr.io/clusterfuzz-images/base
1515

16-
ENV RUN_CMD \
17-
"python3.11 $ROOT_DIR/src/python/other-bots/chromium-tests-syncer/run.py"
16+
ENV RUN_CMD "python3.11 $ROOT_DIR/src/python/bot/startup/run_cron.py chrome_tests_syncer"
1817
ENV DISABLE_MOUNTS True
1918
ENV EXTRA_PATH "/data/depot_tools"
20-
ENV SYNC_INTERVAL 43200
2119
ENV TESTS_ARCHIVE_BUCKET "clusterfuzz-data"
2220
ENV TESTS_ARCHIVE_NAME "web_tests.zip"
2321
ENV TESTS_DIR /home/$USER/tests

docker/chromium/tests-syncer/start.sh

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16-
DEPLOYMENT_ZIP="linux-3.zip"
17-
if [[ $CLUSTERFUZZ_RELEASE == "candidate" ]]; then
18-
DEPLOYMENT_ZIP="linux-3-candidate.zip"
19-
fi
16+
DEPLOYMENT_ZIP="linux-3-chrome-tests-syncer.zip"
17+
2018
export DEPLOYMENT_ZIP
2119

2220
source /data/setup_common.sh

src/clusterfuzz/_internal/base/utils.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,17 +261,19 @@ def _get_manifest_release_suffix(release):
261261
suffix = ''
262262
if sys.version_info.major == 3:
263263
suffix += '.3'
264-
if release == 'candidate':
265-
suffix += '-candidate'
264+
if release == 'prod':
265+
return suffix
266+
suffix += f'-{release}'
266267
return suffix
267268

268269

269270
def _get_deployment_zip_release_suffix(release):
270271
suffix = ''
271272
if sys.version_info.major == 3:
272273
suffix += '-3'
273-
if release == 'candidate':
274-
suffix += '-candidate'
274+
if release == 'prod':
275+
return suffix
276+
suffix += f'-{release}'
275277
return suffix
276278

277279

src/python/other-bots/chromium-tests-syncer/run.py renamed to src/clusterfuzz/_internal/cron/chrome_tests_syncer.py

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,13 @@
2424
import re
2525
import subprocess
2626
import tarfile
27-
import time
2827

2928
from clusterfuzz._internal.base import utils
3029
from clusterfuzz._internal.bot import testcase_manager
3130
from clusterfuzz._internal.bot.tasks import setup
3231
from clusterfuzz._internal.datastore import data_types
33-
from clusterfuzz._internal.datastore import ndb_init
3432
from clusterfuzz._internal.datastore import ndb_utils
3533
from clusterfuzz._internal.metrics import logs
36-
from clusterfuzz._internal.metrics import monitor
3734
from clusterfuzz._internal.metrics import monitoring_metrics
3835
from clusterfuzz._internal.system import archive
3936
from clusterfuzz._internal.system import environment
@@ -338,23 +335,9 @@ def main():
338335
tests_archive_name = environment.get_value('TESTS_ARCHIVE_NAME')
339336
tests_directory = environment.get_value('TESTS_DIR')
340337

341-
# Intervals are in seconds.
342-
sync_interval = environment.get_value('SYNC_INTERVAL')
343-
fail_wait = environment.get_value('FAIL_WAIT')
344-
345-
while True:
346-
sleep_secs = sync_interval
347-
348-
try:
349-
with monitor.wrap_with_monitoring(), ndb_init.context():
350-
sync_tests(tests_archive_bucket, tests_archive_name, tests_directory)
351-
except Exception as e:
352-
logs.error(f'Failed to sync tests: {e}')
353-
sleep_secs = fail_wait
354-
355-
logs.info(f'Sleeping for {sleep_secs} seconds.')
356-
time.sleep(sleep_secs)
357-
358-
359-
if __name__ == '__main__':
360-
main()
338+
try:
339+
sync_tests(tests_archive_bucket, tests_archive_name, tests_directory)
340+
return True
341+
except Exception as e:
342+
logs.error(f'Failed to sync tests: {e}')
343+
return False

src/clusterfuzz/_internal/tests/core/local/butler/deploy_test.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def setUp(self):
5353
os.environ['ROOT_DIR'] = '.'
5454
self.mock.now.return_value = datetime.datetime(2017, 1, 3, 12, 1)
5555
self.manifest_target = 'clusterfuzz-source.manifest.3'
56+
self.additional_manifest_target = 'clusterfuzz-source.manifest.3-chrome-tests-syncer'
5657

5758
def _check_env_variables(self, yaml_paths):
5859
"""Check that environment variables are written to yaml paths."""
@@ -182,6 +183,10 @@ def test_app(self):
182183
mock.call('gsutil cp src/appengine/resources/'
183184
'clusterfuzz-source.manifest '
184185
'gs://test-deployment-bucket/' + self.manifest_target),
186+
mock.call('gsutil cp src/appengine/resources/'
187+
'clusterfuzz-source.manifest '
188+
'gs://test-deployment-bucket/' +
189+
self.additional_manifest_target),
185190
mock.call('python butler.py run setup --config-dir /config_dir '
186191
'--non-dry-run'),
187192
])
@@ -264,6 +269,10 @@ def test_app_retry(self):
264269
mock.call('gsutil cp src/appengine/resources/'
265270
'clusterfuzz-source.manifest '
266271
'gs://test-deployment-bucket/' + self.manifest_target),
272+
mock.call('gsutil cp src/appengine/resources/'
273+
'clusterfuzz-source.manifest '
274+
'gs://test-deployment-bucket/' +
275+
self.additional_manifest_target),
267276
mock.call('python butler.py run setup --config-dir /config_dir '
268277
'--non-dry-run'),
269278
])

src/local/butler/constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
('linux', ('manylinux2014_x86_64')),
4242
])
4343

44+
# Additional required packages when deploying to prod.
45+
ADDITIONAL_RELEASES = ['chrome-tests-syncer']
46+
4447
if sys.version_info.major == 3 and sys.version_info.minor == 7:
4548
ABIS = {'linux': 'cp37m', 'windows': 'cp37m', 'macos': 'cp37m'}
4649
elif sys.version_info.major == 3 and sys.version_info.minor == 8:

src/local/butler/deploy.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,14 @@ def _deploy_app_prod(project,
117117
_deploy_zip(
118118
deployment_bucket, package_zip_path, test_deployment=test_deployment)
119119

120-
_deploy_manifest(
121-
deployment_bucket,
122-
constants.PACKAGE_TARGET_MANIFEST_PATH,
123-
test_deployment=test_deployment,
124-
release=release)
120+
releases = [release]
121+
releases += constants.ADDITIONAL_RELEASES if release == 'prod' else []
122+
for rel in releases:
123+
_deploy_manifest(
124+
deployment_bucket,
125+
constants.PACKAGE_TARGET_MANIFEST_PATH,
126+
test_deployment=test_deployment,
127+
release=rel)
125128

126129

127130
def _deploy_app_staging(project, yaml_paths):
@@ -563,9 +566,8 @@ def execute(args):
563566
package_zip_paths = []
564567
if deploy_zips:
565568
for platform_name in platforms:
566-
package_zip_paths.append(
567-
package.package(
568-
revision, platform_name=platform_name, release=args.release))
569+
package_zip_paths += package.package(
570+
revision, platform_name=platform_name, release=args.release)
569571
else:
570572
# package.package calls these, so only set these up if we're not packaging,
571573
# since they can be fairly slow.

src/local/butler/package.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import os
1717
import re
18+
import shutil
1819
import sys
1920
import zipfile
2021

@@ -135,7 +136,20 @@ def package(revision,
135136

136137
print()
137138
print('%s is ready.' % target_zip_path)
138-
return target_zip_path
139+
140+
targets_zip_paths = [target_zip_path]
141+
if platform_name and release == 'prod':
142+
# Copy prod package into additional releases.
143+
for add_release in constants.ADDITIONAL_RELEASES:
144+
add_target_zip_name = utils.get_platform_deployment_filename(
145+
platform_name, release=add_release)
146+
add_target_zip_path = os.path.join(target_zip_dir, add_target_zip_name)
147+
_clear_zip(add_target_zip_path)
148+
shutil.copy2(target_zip_path, add_target_zip_path)
149+
print('\n%s is ready.' % add_target_zip_path)
150+
targets_zip_paths.append(add_target_zip_path)
151+
152+
return targets_zip_paths
139153

140154

141155
def execute(args):

0 commit comments

Comments
 (0)