def run(self, request, class_path): if not request.user.has_perm("extras.run_job"): raise PermissionDenied( "This user does not have permission to run jobs.") job_class = self._get_job_class(class_path) job = job_class() input_serializer = serializers.JobInputSerializer(data=request.data) input_serializer.is_valid(raise_exception=True) data = input_serializer.data["data"] or {} commit = input_serializer.data["commit"] if commit is None: commit = getattr(job_class.Meta, "commit_default", True) try: job.validate_data(data) except FormsValidationError as e: # message_dict can only be accessed if ValidationError got a dict # in the constructor (saved as error_dict). Otherwise we get a list # of errors under messages return Response( { "errors": e.message_dict if hasattr(e, "error_dict") else e.messages }, status=400) if not get_worker_count(): raise CeleryWorkerNotRunningException() job_content_type = ContentType.objects.get(app_label="extras", model="job") schedule = input_serializer.data.get("schedule") if schedule: schedule = self._create_schedule(schedule, data, commit, job, job_class, request) else: job_result = JobResult.enqueue_job( run_job, job.class_path, job_content_type, request.user, data=data, request=copy_safe_request(request), commit=commit, ) job.result = job_result serializer = serializers.JobDetailSerializer( job, context={"request": request}) return Response(serializer.data)
def sync(self, request, pk): """ Enqueue pull git repository and refresh data. """ if not request.user.has_perm("extras.change_gitrepository"): raise PermissionDenied("This user does not have permission to make changes to Git repositories.") if not get_worker_count(): raise CeleryWorkerNotRunningException() repository = get_object_or_404(GitRepository, id=pk) enqueue_pull_git_repository_and_refresh_data(repository, request) return Response({"message": f"Repository {repository} sync job added to queue."})
def post(self, request, slug): if not request.user.has_perm("extras.change_gitrepository"): return HttpResponseForbidden() repository = get_object_or_404(GitRepository.objects.all(), slug=slug) # Allow execution only if a worker process is running. if not get_worker_count(request): messages.error(request, "Unable to run job: Celery worker process not running.") else: enqueue_pull_git_repository_and_refresh_data(repository, request) return redirect("extras:gitrepository_result", slug=slug)
def post(self, request, class_path): if not request.user.has_perm("extras.run_job"): return HttpResponseForbidden() job_class = get_job(class_path) if job_class is None: raise Http404 job = job_class() grouping, module, class_name = class_path.split("/", 2) form = job.as_form(request.POST, request.FILES) # Allow execution only if a worker process is running. if not get_worker_count(request): messages.error( request, "Unable to run job: Celery worker process not running.") elif form.is_valid(): # Run the job. A new JobResult is created. commit = form.cleaned_data.pop("_commit") job_content_type = ContentType.objects.get(app_label="extras", model="job") job_result = JobResult.enqueue_job( run_job, job.class_path, job_content_type, request.user, data=job_class.serialize_data(form.cleaned_data), request=copy_safe_request(request), commit=commit, ) return redirect("extras:job_jobresult", pk=job_result.pk) return render( request, "extras/job.html", { "grouping": grouping, "module": module, "job": job, "form": form, }, )
def _run_job(request, job_model, legacy_response=False): """An internal function providing logic shared between JobModelViewSet.run() and JobViewSet.run().""" if not request.user.has_perm("extras.run_job"): raise PermissionDenied("This user does not have permission to run jobs.") if not job_model.enabled: raise PermissionDenied("This job is not enabled to be run.") if not job_model.installed: raise MethodNotAllowed(request.method, detail="This job is not presently installed and cannot be run") job_class = job_model.job_class if job_class is None: raise MethodNotAllowed(request.method, detail="This job's source code could not be located and cannot be run") job = job_class() input_serializer = serializers.JobInputSerializer(data=request.data) input_serializer.is_valid(raise_exception=True) data = input_serializer.data["data"] or {} commit = input_serializer.data["commit"] if commit is None: commit = job_model.commit_default try: job.validate_data(data) except FormsValidationError as e: # message_dict can only be accessed if ValidationError got a dict # in the constructor (saved as error_dict). Otherwise we get a list # of errors under messages return Response({"errors": e.message_dict if hasattr(e, "error_dict") else e.messages}, status=400) if not get_worker_count(): raise CeleryWorkerNotRunningException() job_content_type = get_job_content_type() schedule_data = input_serializer.data.get("schedule") # Default to a null JobResult. job_result = None # Assert that a job with `approval_required=True` has a schedule that enforces approval and # executes immediately. if schedule_data is None and job_model.approval_required: schedule_data = {"interval": JobExecutionType.TYPE_IMMEDIATELY} # Try to create a ScheduledJob, or... if schedule_data: schedule = _create_schedule(schedule_data, data, commit, job, job_model, request) else: schedule = None # ... If we can't create one, create a JobResult instead. if schedule is None: job_result = JobResult.enqueue_job( run_job, job.class_path, job_content_type, request.user, data=data, request=copy_safe_request(request), commit=commit, ) job.result = job_result if legacy_response: # Old-style JobViewSet response - serialize the Job class in the response for some reason? serializer = serializers.JobClassDetailSerializer(job, context={"request": request}) return Response(serializer.data) else: # New-style JobModelViewSet response - serialize the schedule or job_result as appropriate data = {"schedule": None, "job_result": None} if schedule: data["schedule"] = nested_serializers.NestedScheduledJobSerializer( schedule, context={"request": request} ).data if job_result: data["job_result"] = nested_serializers.NestedJobResultSerializer( job_result, context={"request": request} ).data return Response(data, status=status.HTTP_201_CREATED)