def cancel_event(request, event_id): """ Cancel all jobs attached to an event """ if request.method != 'POST': return HttpResponseNotAllowed(['POST']) ev = get_object_or_404(models.Event, pk=event_id) allowed = Permissions.is_collaborator(request.session, ev.build_user, ev.base.repo()) if not allowed: messages.error(request, 'You are not allowed to cancel this event') return redirect('ci:view_event', event_id=ev.pk) signed_in_user = ev.base.server().signed_in_user(request.session) comment = escape(request.POST.get("comment")) post_to_pr = request.POST.get("post_to_pr") == "on" event_url = reverse("ci:view_event", args=[ev.pk]) message = "Parent <a href='%s'>event</a> canceled by %s" % (event_url, signed_in_user) if comment: message += " with comment: %s" % comment if post_to_pr: post_event_change_to_pr(request, ev, "canceled", comment, signed_in_user) event.cancel_event(ev, message, True) logger.info('Event {}: {} canceled by {}'.format(ev.pk, ev, signed_in_user)) messages.info(request, 'Event {} canceled'.format(ev)) return redirect('ci:view_event', event_id=ev.pk)
def activate_job(request, job_id): """ Endpoint for activating a job """ if request.method != 'POST': return HttpResponseNotAllowed(['POST']) job = get_object_or_404(models.Job, pk=job_id) server = job.recipe.repository.server() user = server.signed_in_user(request.session) if not user: raise PermissionDenied('You need to be signed in to activate a job') collab = Permissions.is_collaborator(request.session, job.event.build_user, job.recipe.repository, user=user) if collab: if set_job_active(request, job, user): job.init_pr_status() job.event.make_jobs_ready() else: raise PermissionDenied( 'Activate job: {} is NOT a collaborator on {}'.format( user, job.recipe.repository)) return redirect('ci:view_job', job_id=job.pk)
def activate_event(request, event_id): """ Endpoint for activating all jobs on an event """ if request.method != 'POST': return HttpResponseNotAllowed(['POST']) ev = get_object_or_404(models.Event, pk=event_id) jobs = ev.jobs.filter(active=False).order_by('-created') if jobs.count() == 0: messages.info(request, 'No jobs to activate') return redirect('ci:view_event', event_id=ev.pk) repo = jobs.first().recipe.repository user = repo.server().signed_in_user(request.session) if not user: raise PermissionDenied('You need to be signed in to activate jobs') collab = Permissions.is_collaborator(request.session, ev.build_user, repo, user=user) if collab: activated_jobs = [] for j in jobs.all(): if set_job_active(request, j, user): activated_jobs.append(j) for j in activated_jobs: j.init_pr_status() ev.make_jobs_ready() else: raise PermissionDenied('Activate event: {} is NOT a collaborator on {}'.format(user, repo)) return redirect('ci:view_event', event_id=ev.pk)
def cancel_job(request, job_id): if request.method != 'POST': return HttpResponseNotAllowed(['POST']) job = get_object_or_404(models.Job, pk=job_id) allowed = Permissions.is_collaborator(request.session, job.event.build_user, job.event.base.repo()) if not allowed: return HttpResponseForbidden('Not allowed to cancel this job') signed_in_user = job.event.base.server().signed_in_user(request.session) message = "Canceled by %s" % signed_in_user comment = escape(request.POST.get('comment')) post_to_pr = request.POST.get('post_to_pr') == 'on' if post_to_pr: post_job_change_to_pr(request, job, "canceled", comment, signed_in_user) if comment: message += "\nwith comment: %s" % comment set_job_canceled(job, message) UpdateRemoteStatus.job_complete(job) logger.info('Job {}: {} on {} canceled by {}'.format( job.pk, job, job.recipe.repository, signed_in_user)) messages.info(request, 'Job {} canceled'.format(job)) return redirect('ci:view_job', job_id=job.pk)
def view_event(request, event_id): """ Show the details of an Event """ ev = get_object_or_404(EventsStatus.events_with_head(), pk=event_id) evs_info = EventsStatus.multiline_events_info([ev]) allowed = Permissions.is_collaborator(request.session, ev.build_user, ev.base.repo()) has_unactivated = ev.jobs.filter(active=False).count() != 0 context = {'event': ev, 'events': evs_info, 'allowed_to_cancel': allowed, "update_interval": settings.EVENT_PAGE_UPDATE_INTERVAL, "has_unactivated": has_unactivated, } return render(request, 'ci/event.html', context)
def invalidate_event(request, event_id): """ Invalidate all the jobs of an event. The user must be signed in. Input: request: django.http.HttpRequest event_id. models.Event.pk: PK of the event to be invalidated Return: django.http.HttpResponse based object """ if request.method != 'POST': return HttpResponseNotAllowed(['POST']) ev = get_object_or_404(models.Event, pk=event_id) allowed = Permissions.is_collaborator(request.session, ev.build_user, ev.base.repo()) if not allowed: messages.error( request, 'You need to be signed in and be a collaborator to invalidate results.' ) return redirect('ci:view_event', event_id=ev.pk) signed_in_user = ev.base.server().signed_in_user(request.session) comment = escape(request.POST.get("comment")) logger.info('Event {}: {} invalidated by {}'.format( ev.pk, ev, signed_in_user)) event_url = reverse("ci:view_event", args=[ev.pk]) message = "Parent <a href='%s'>event</a> invalidated by %s" % ( event_url, signed_in_user) if comment: message += " with comment: %s" % comment post_to_pr = request.POST.get("post_to_pr") == "on" if post_to_pr: post_event_change_to_pr(request, ev, "invalidated", comment, signed_in_user) same_client = request.POST.get('same_client') == "on" for job in ev.jobs.all(): invalidate_job(request, job, message, same_client, check_ready=False) # Only do this once so that we get the job dependencies setup correctly. ev.make_jobs_ready() return redirect('ci:view_event', event_id=ev.pk)
def invalidate(request, job_id): """ Invalidate the results of a Job. The user must be signed in. Input: request: django.http.HttpRequest job_id: models.Job.pk """ if request.method != 'POST': return HttpResponseNotAllowed(['POST']) job = get_object_or_404(models.Job, pk=job_id) allowed = Permissions.is_collaborator(request.session, job.event.build_user, job.event.base.repo()) if not allowed: raise PermissionDenied('You are not allowed to invalidate results.') same_client = request.POST.get('same_client') == 'on' selected_client = request.POST.get('client_list') comment = escape(request.POST.get('comment')) post_to_pr = request.POST.get('post_to_pr') == 'on' client = None if selected_client: try: client = models.Client.objects.get(pk=int(selected_client)) same_client = True except: pass signed_in_user = job.event.base.server().signed_in_user(request.session) message = "Invalidated by %s" % signed_in_user if comment: message += "\nwith comment: %s" % comment if post_to_pr: post_job_change_to_pr(request, job, "invalidated", comment, signed_in_user) logger.info('Job {}: {} on {} invalidated by {}'.format( job.pk, job, job.recipe.repository, signed_in_user)) invalidate_job(request, job, message, same_client, client) return redirect('ci:view_job', job_id=job.pk)
def update_remote_job_status(request, job_id): """ End point for manually update the remote status of a job. This is needed since sometimes the git server doesn't get updated properly due to timeouts, etc. """ job = get_object_or_404(models.Job.objects, pk=job_id) allowed = Permissions.is_collaborator(request.session, job.event.build_user, job.event.base.repo()) if request.method == "GET": return render(request, 'ci/job_update.html', { "job": job, "allowed": allowed }) elif request.method == "POST": if allowed: UpdateRemoteStatus.job_complete_pr_status(job) else: return HttpResponseNotAllowed("Not allowed") return redirect('ci:view_job', job_id=job.pk)
def view_pr(request, pr_id): """ Show the details of a PR Input: request: django.http.HttpRequest pr_id: pk of models.PullRequest Return: django.http.HttpResponse based object """ pr = get_object_or_404(models.PullRequest.objects.select_related('repository__user'), pk=pr_id) ev = pr.events.select_related('build_user', 'base__branch__repository__user__server').latest() allowed = Permissions.is_collaborator(request.session, ev.build_user, ev.base.repo()) current_alt = [] alt_choices = [] default_choices = [] if allowed: alt_recipes = (models.Recipe.objects .filter(repository=pr.repository, build_user=ev.build_user, current=True, active=True, cause=models.Recipe.CAUSE_PULL_REQUEST_ALT,) .order_by("display_name")) default_recipes = (models.Recipe.objects .filter(repository=pr.repository, build_user=ev.build_user, current=True, active=True, cause=models.Recipe.CAUSE_PULL_REQUEST,) .order_by("display_name")) push_recipes = (models.Recipe.objects .filter(repository=pr.repository, build_user=ev.build_user, current=True, active=True, cause=models.Recipe.CAUSE_PUSH,) .order_by("display_name")) default_recipes = [r for r in default_recipes.all()] current_alt = [ r.pk for r in pr.alternate_recipes.all() ] current_default = [j.recipe.filename for j in pr.events.latest("created").jobs.all() ] push_map = {r.filename: r.branch for r in push_recipes.all()} alt_choices = [] for r in alt_recipes: alt_choices.append({"recipe": r, "selected": r.pk in current_alt, "push_branch": push_map.get(r.filename), }) default_choices = [] for r in default_recipes: default_choices.append({"recipe": r, "pk": r.pk, "disabled": r.filename in current_default, "push_branch": push_map.get(r.filename), }) if alt_choices and request.method == "POST": form_choices = [ (r.pk, r.display_name) for r in alt_recipes ] form = forms.AlternateRecipesForm(request.POST) form.fields["recipes"].choices = form_choices form_default_choices = [] for r in default_choices: if not r["disabled"]: form_default_choices.append((r["pk"], r["recipe"].display_name)) form.fields["default_recipes"].choices = form_default_choices if form.is_valid(): pr.alternate_recipes.clear() for pk in form.cleaned_data["recipes"]: alt = models.Recipe.objects.get(pk=pk) pr.alternate_recipes.add(alt) # do some saves to update the timestamp so that the javascript updater gets activated pr.save() pr.events.latest('created').save() messages.info(request, "Success") pr_event = PullRequestEvent.PullRequestEvent() selected_default_recipes = [] if form.cleaned_data["default_recipes"]: q = models.Recipe.objects.filter(pk__in=form.cleaned_data["default_recipes"]) selected_default_recipes = [r for r in q] pr_event.create_pr_alternates(pr, default_recipes=selected_default_recipes) # update the choices so the new form is correct current_alt = [ r.pk for r in pr.alternate_recipes.all() ] alt_choices = [ {"recipe": r, "selected": r.pk in current_alt} for r in alt_recipes ] else: messages.warning(request, "Invalid form") logger.warning("Invalid form") for field, errors in form.errors.items(): logger.warning("Form error in field: %s: %s" % (field, errors)) events = EventsStatus.events_with_head(pr.events) evs_info = EventsStatus.multiline_events_info(events, events_url=True) context = { "pr": pr, "events": evs_info, "allowed": allowed, "update_interval": settings.EVENT_PAGE_UPDATE_INTERVAL, "alt_choices": alt_choices, "default_choices": default_choices, } return render(request, 'ci/pr.html', context)
def _check_recipe(self, session, git_api, pr, ev, recipe): """ Check if an individual recipe is active for the PR. If it is not then set a comment on the PR saying that they need to activate the recipe. Input: session[dict]: Session to store collaborator information git_api[GitAPI]: Git API for the build_user pr: models.PullRequest that we are processing ev: models.Event that is attached to this pull request recipe: models.Recipe that we need to process """ if not recipe.active: return [] active = False server = pr.repository.user.server if recipe.automatic == models.Recipe.FULL_AUTO: active = True elif recipe.automatic == models.Recipe.MANUAL: active = False elif recipe.automatic == models.Recipe.AUTO_FOR_AUTHORIZED: if ev.trigger_user: pr_user, created = models.GitUser.objects.get_or_create( name=ev.trigger_user, server=server) if pr_user in recipe.auto_authorized.all(): active = True else: active = Permissions.is_collaborator(session, recipe.build_user, recipe.repository, user=pr_user) if active: logger.info( 'User {} is allowed to activate recipe: {}: {}'.format( pr_user, recipe.pk, recipe)) else: logger.info( 'User {} is NOT allowed to activate recipe {}: {}'. format(pr_user, recipe.pk, recipe)) if created: pr_user.delete() else: logger.info( 'Recipe: {}: {}: not activated because trigger_user is blank' .format(recipe.pk, recipe)) jobs = [] for config in recipe.build_configs.order_by("name").all(): job, created = models.Job.objects.get_or_create(recipe=recipe, event=ev, config=config) if created: job.active = active job.ready = False job.complete = False if job.active: job.status = models.JobStatus.NOT_STARTED else: job.status = models.JobStatus.ACTIVATION_REQUIRED job.save() logger.info('Created job {}: {}: on {}'.format( job.pk, job, recipe.repository)) jobs.append(job) else: logger.info('Job {}: {}: on {} already exists'.format( job.pk, job, recipe.repository)) return jobs