diff --git a/src/cli/onefuzz/api.py b/src/cli/onefuzz/api.py index 23c2f4a95..8ecf47fa6 100644 --- a/src/cli/onefuzz/api.py +++ b/src/cli/onefuzz/api.py @@ -970,6 +970,61 @@ class JobContainers(Endpoint): results[container] = self.onefuzz.containers.files.list(container).files return results + def delete( + self, + job_id: UUID_EXPANSION, + *, + only_job_specific: bool = True, + dryrun: bool = False, + ) -> None: + SAFE_TO_REMOVE = [ + enums.ContainerType.crashes, + enums.ContainerType.setup, + enums.ContainerType.inputs, + enums.ContainerType.reports, + enums.ContainerType.unique_inputs, + enums.ContainerType.unique_reports, + enums.ContainerType.no_repro, + enums.ContainerType.analysis, + enums.ContainerType.coverage, + enums.ContainerType.readonly_inputs, + enums.ContainerType.regression_reports, + ] + + job = self.onefuzz.jobs.get(job_id) + containers = set() + to_delete = set() + for task in self.onefuzz.jobs.tasks.list(job_id=job.job_id): + for container in task.config.containers: + containers.add(container.name) + if container.type not in SAFE_TO_REMOVE: + continue + elif not only_job_specific: + to_delete.add(container.name) + elif only_job_specific and ( + self.onefuzz.utils.build_container_name( + container_type=container.type, + project=job.config.project, + name=job.config.name, + build=job.config.build, + platform=task.os, + ) + == container.name + ): + to_delete.add(container.name) + + to_keep = containers - to_delete + for container_name in to_keep: + self.logger.info("not removing: %s", container_name) + + for container_name in to_delete: + if dryrun: + self.logger.info("container would be deleted: %s", container_name) + elif self.onefuzz.containers.delete(container_name).result: + self.logger.info("removed container: %s", container_name) + else: + self.logger.info("container already removed: %s", container_name) + class JobTasks(Endpoint): """Interact with tasks within a job""" @@ -1495,6 +1550,39 @@ class Utils(Command): identifiers.append(platform) return uuid.uuid5(ONEFUZZ_GUID_NAMESPACE, ":".join(identifiers)) + def build_container_name( + self, + *, + container_type: enums.ContainerType, + project: str, + name: str, + build: str, + platform: enums.OS, + ) -> primitives.Container: + if container_type in [enums.ContainerType.setup, enums.ContainerType.coverage]: + guid = self.namespaced_guid( + project, + name, + build=build, + platform=platform.name, + ) + elif container_type == enums.ContainerType.regression_reports: + guid = self.namespaced_guid( + project, + name, + build=build, + ) + else: + guid = self.namespaced_guid(project, name) + + return primitives.Container( + "oft-%s-%s" + % ( + container_type.name.replace("_", "-"), + guid.hex, + ) + ) + class Onefuzz: def __init__( diff --git a/src/cli/onefuzz/job_templates/handlers.py b/src/cli/onefuzz/job_templates/handlers.py index 45fc70e04..f1bee92c3 100644 --- a/src/cli/onefuzz/job_templates/handlers.py +++ b/src/cli/onefuzz/job_templates/handlers.py @@ -11,7 +11,6 @@ from onefuzztypes.job_templates import JobTemplateConfig, JobTemplateRequest from onefuzztypes.models import Job, TaskContainers from ..api import Endpoint -from ..templates import _build_container_name from .job_monitor import JobMonitor @@ -75,13 +74,12 @@ class TemplateSubmitHandler(Endpoint): raise TypeError if not isinstance(request.user_fields["build"], str): raise TypeError - container_name = _build_container_name( - self.onefuzz, - container_type, - request.user_fields["project"], - request.user_fields["name"], - request.user_fields["build"], - config.os, + container_name = self.onefuzz.utils.build_container_name( + container_type=container_type, + project=request.user_fields["project"], + name=request.user_fields["name"], + build=request.user_fields["build"], + platform=config.os, ) request.containers.append( TaskContainers(name=container_name, type=container_type) diff --git a/src/cli/onefuzz/template.py b/src/cli/onefuzz/template.py index c1ffa922e..416db39e1 100644 --- a/src/cli/onefuzz/template.py +++ b/src/cli/onefuzz/template.py @@ -6,8 +6,6 @@ import logging from typing import Optional -from onefuzztypes.enums import ContainerType - from .api import Command, Onefuzz from .templates.afl import AFL from .templates.libfuzzer import Libfuzzer @@ -35,19 +33,6 @@ class Template(Command): delete_containers: bool = False, stop_notifications: bool = False, ) -> None: - SAFE_TO_REMOVE = [ - ContainerType.crashes, - ContainerType.setup, - ContainerType.inputs, - ContainerType.reports, - ContainerType.unique_inputs, - ContainerType.unique_reports, - ContainerType.no_repro, - ContainerType.analysis, - ContainerType.coverage, - ContainerType.readonly_inputs, - ] - msg = ["project:%s" % project, "name:%s" % name] if build is not None: msg.append("build:%s" % build) @@ -68,23 +53,15 @@ class Template(Command): self.logger.info("stopping job: %s", job.job_id) self.onefuzz.jobs.delete(job.job_id) + if delete_containers: + self.onefuzz.jobs.containers.delete(job.job_id) + tasks = self.onefuzz.tasks.list(job_id=job.job_id) for task in tasks: if task.state not in ["stopped"]: self.logger.info("stopping task: %s", task.task_id) self.onefuzz.tasks.delete(task.task_id) - if delete_containers: - to_remove = [] - for container in task.config.containers: - if container.type not in SAFE_TO_REMOVE: - self.logger.info("not removing: %s", container) - continue - to_remove.append(container.name) - for container_name in to_remove: - if self.onefuzz.containers.delete(container_name).result: - self.logger.info("removed container: %s", container_name) - if stop_notifications: notifications = self.onefuzz.notifications.list() for container in task.config.containers: diff --git a/src/cli/onefuzz/templates/__init__.py b/src/cli/onefuzz/templates/__init__.py index 4b660833e..532e05322 100644 --- a/src/cli/onefuzz/templates/__init__.py +++ b/src/cli/onefuzz/templates/__init__.py @@ -25,39 +25,6 @@ class StoppedEarly(Exception): pass -def _build_container_name( - onefuzz: "Onefuzz", - container_type: ContainerType, - project: str, - name: str, - build: str, - platform: OS, -) -> Container: - if container_type in [ContainerType.setup, ContainerType.coverage]: - guid = onefuzz.utils.namespaced_guid( - project, - name, - build=build, - platform=platform.name, - ) - elif container_type == ContainerType.regression_reports: - guid = onefuzz.utils.namespaced_guid( - project, - name, - build=build, - ) - else: - guid = onefuzz.utils.namespaced_guid(project, name) - - return Container( - "oft-%s-%s" - % ( - container_type.name.replace("_", "-"), - guid.hex, - ) - ) - - class JobHelper: def __init__( self, @@ -115,13 +82,12 @@ class JobHelper: """ for container_type in types: - self.containers[container_type] = _build_container_name( - self.onefuzz, - container_type, - self.project, - self.name, - self.build, - self.platform, + self.containers[container_type] = self.onefuzz.utils.build_container_name( + container_type=container_type, + project=self.project, + name=self.name, + build=self.build, + platform=self.platform, ) def get_unique_container_name(self, container_type: ContainerType) -> Container: