diff --git a/src/andromeda/apps.py b/src/andromeda/apps.py index 89181bf9..7006d77f 100644 --- a/src/andromeda/apps.py +++ b/src/andromeda/apps.py @@ -1,5 +1,9 @@ +"""App for the andromeda integration.""" + from django.apps import AppConfig class AndromedaConfig(AppConfig): + """AppConfig for the andromeda integration.""" + name = "andromeda" diff --git a/src/andromeda/client.py b/src/andromeda/client.py index 7a2cc1da..816dcc53 100644 --- a/src/andromeda/client.py +++ b/src/andromeda/client.py @@ -1,3 +1,5 @@ +"""A simple andromeda API client.""" + from uuid import UUID import requests @@ -8,6 +10,7 @@ def post(path, **kwargs): + """Send a post request to andromeda.""" response = requests.post( f"{settings.ANDROMEDA_URL}/{path}", headers={"Authorization": settings.ANDROMEDA_API_KEY}, @@ -20,6 +23,7 @@ def post(path, **kwargs): def get(path, **kwargs): + """Send a get request to andromeda.""" response = requests.get( f"{settings.ANDROMEDA_URL}/{path}", headers={"Authorization": settings.ANDROMEDA_API_KEY}, @@ -32,18 +36,22 @@ def get(path, **kwargs): def get_instance(user_id, job_id): + """Get a challenge instance of a given job id for a user.""" return post("", json={"user": str(user_id), "job": job_id}) def request_reset(user_id, job_id): + """Reset a challenge instance of a given job id for a user.""" return post("reset", json={"user": str(user_id), "job": job_id}) def list_jobs(): + """Get a list of all jobs running on the andromeda host.""" return get("jobs") def restart_job(job_id): + """Restarts a job with a given uuid.""" try: UUID(job_id) except ValueError: @@ -52,12 +60,15 @@ def restart_job(job_id): def list_instances(): + """List all the instances of challenges on the andromeda host.""" return get("instances") def sysinfo(): + """Get the current system info of the andromeda host.""" return get("sysinfo") def submit_job(job_spec): + """Submit a job to the andromeda host.""" return post("job/submit", json=job_spec) diff --git a/src/andromeda/serializers.py b/src/andromeda/serializers.py index 6386cc68..ddc4ff42 100644 --- a/src/andromeda/serializers.py +++ b/src/andromeda/serializers.py @@ -1,10 +1,16 @@ +"""Serializers for the andromeda integration.""" + from rest_framework import serializers class JobSubmitSerializer(serializers.Serializer): + """Serializer for job submissions associated with a challenge id.""" + challenge_id = serializers.IntegerField() job_spec = serializers.JSONField() class JobSubmitRawSerializer(serializers.Serializer): + """Serializer for job submissions.""" + job_spec = serializers.JSONField() diff --git a/src/andromeda/urls.py b/src/andromeda/urls.py index ca4feca5..af2b14b7 100644 --- a/src/andromeda/urls.py +++ b/src/andromeda/urls.py @@ -1,3 +1,4 @@ +"""URL routes for the andromeda integration.""" from django.urls import path from andromeda import views diff --git a/src/andromeda/views.py b/src/andromeda/views.py index 7e4d58d0..a3d6f8db 100644 --- a/src/andromeda/views.py +++ b/src/andromeda/views.py @@ -1,3 +1,5 @@ +"""API endpoints for the andromeda integration.""" + from django.conf import settings from rest_framework.generics import get_object_or_404 from rest_framework.permissions import IsAdminUser, IsAuthenticated @@ -5,17 +7,20 @@ from rest_framework.views import APIView from andromeda import client -from andromeda.serializers import JobSubmitSerializer +from andromeda.serializers import JobSubmitSerializer, JobSubmitRawSerializer from backend.response import FormattedResponse from challenge.models import Challenge from challenge.permissions import CompetitionOpen class GetInstanceView(APIView): + """Endpoint for getting an instance of a given challenge.""" + permission_classes = (IsAuthenticated, CompetitionOpen) throttle_scope = "challenge_instance_get" def get(self, request, job_id): + """Given a job id, return an instance of the relevant challenge for this user.""" if not settings.CHALLENGE_SERVER_ENABLED: return FormattedResponse(m="challenge_server_disabled", status=HTTP_403_FORBIDDEN) if not request.user.team: @@ -24,52 +29,69 @@ def get(self, request, job_id): class ResetInstanceView(APIView): + """Endpoint for resetting an instance of a given challenge.""" + permission_classes = (IsAuthenticated,) throttle_scope = "challenge_instance_reset" def get(self, request, job_id): + """Given a job id, return a new instance of the relevant challenge for this user.""" if not settings.CHALLENGE_SERVER_ENABLED: return FormattedResponse(m="challenge_server_disabled", status=HTTP_403_FORBIDDEN) return FormattedResponse(client.request_reset(request.user.team.pk, job_id)) class ListJobsView(APIView): + """Endpoint for listing the jobs on the andromeda host.""" permission_classes = (IsAdminUser,) throttle_scope = "andromeda_view_jobs" def get(self, request): + """Return a list of all jobs that have been submitted to an andromeda host.""" return FormattedResponse(client.list_jobs()) class RestartJobView(APIView): + """Endpoint for restarting a job on the andromeda host.""" + permission_classes = (IsAdminUser,) throttle_scope = "andromeda_manage_jobs" def post(self, request): + """Given a job id, restart all instances of that challenge on the andromeda host.""" return FormattedResponse(client.restart_job(request.data["job_id"])) class ListInstancesView(APIView): + """Endpoint for listing all instances of challenges on the andromeda host.""" + permission_classes = (IsAdminUser,) throttle_scope = "andromeda_view_jobs" def get(self, request): + """Get a list of all challenge instances on the andromeda host.""" return FormattedResponse(client.list_instances()) class SysinfoView(APIView): + """Endpoint for getting the system info of an andromeda host.""" + permission_classes = (IsAdminUser,) throttle_scope = "andromeda_view_sysinfo" def get(self, request): + """Get the reported system info(free ram, cpu, etc) of the andromeda host.""" return FormattedResponse(client.sysinfo()) class JobSubmitView(APIView): + """Endpoint to submit a job to the andromeda host and link it to a given challenge.""" + permission_classes = (IsAdminUser,) throttle_scope = "andromeda_manage_jobs" def post(self, request): + """Submit a job to the andromeda host then add it to the challenge's challenge_metadata.""" serializer = JobSubmitSerializer(request.data) challenge = get_object_or_404(Challenge.objects, id=serializer.data["challenge_id"]) response = client.submit_job(serializer.data["job_spec"]) @@ -79,10 +101,13 @@ def post(self, request): class JobSubmitRawView(APIView): + """Endpoint to submit a job to the andromeda host.""" + permission_classes = (IsAdminUser,) throttle_scope = "andromeda_manage_jobs" def post(self, request): - serializer = JobSubmitSerializer(request.data) + """Submit a job to the andromeda host.""" + serializer = JobSubmitRawSerializer(request.data) response = client.submit_job(serializer.data["job_spec"]) return FormattedResponse(response)