Skip to content

Commit

Permalink
Refactorization for run_existing end-point (#1267)
Browse files Browse the repository at this point in the history
* Saving job config

* Updated run_existing method

* Applying tests to the serializers

* Transform config to JSON

* Uploaded tests

* Fixed code format

* Improve swagger documentation

* Added logger to the upload end-point

* Reorder upload program serializer

* Reorder job serializer

* Added loos for run_existing

* Fix typo

* Updated migration script
  • Loading branch information
Tansito authored Apr 10, 2024
1 parent cc078f1 commit 10f6b19
Show file tree
Hide file tree
Showing 10 changed files with 322 additions and 95 deletions.
29 changes: 29 additions & 0 deletions gateway/api/migrations/0019_alter_job_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Generated by Django 4.2.11 on 2024-04-09 20:48

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("api", "0018_remove_program_public_delete_catalogentry"),
]

operations = [
migrations.AlterField(
model_name="job",
name="status",
field=models.CharField(
choices=[
("PENDING", "Pending"),
("RUNNING", "Running"),
("STOPPED", "Stopped"),
("SUCCEEDED", "Succeeded"),
("QUEUED", "Queued"),
("FAILED", "Failed"),
],
default="QUEUED",
max_length=10,
),
),
]
10 changes: 5 additions & 5 deletions gateway/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class JobConfig(models.Model):
"""Job Configuration model."""

id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created = models.DateTimeField(auto_now_add=True)
created = models.DateTimeField(auto_now_add=True, editable=False)

auto_scaling = models.BooleanField(default=False, null=True)
workers = models.IntegerField(
Expand Down Expand Up @@ -48,7 +48,7 @@ class JobConfig(models.Model):
)

def __str__(self):
return self.id
return f"{self.id}"


class Program(ExportModelOperationsMixin("program"), models.Model):
Expand Down Expand Up @@ -123,7 +123,7 @@ class Job(models.Model):
RUNNING_STATES = [RUNNING, PENDING]

id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created = models.DateTimeField(auto_now_add=True)
created = models.DateTimeField(auto_now_add=True, editable=False)
updated = models.DateTimeField(auto_now=True, null=True)

program = models.ForeignKey(to=Program, on_delete=models.SET_NULL, null=True)
Expand All @@ -137,7 +137,7 @@ class Job(models.Model):
status = models.CharField(
max_length=10,
choices=JOB_STATUSES,
default=PENDING,
default=QUEUED,
)
compute_resource = models.ForeignKey(
ComputeResource, on_delete=models.SET_NULL, null=True, blank=True
Expand All @@ -156,7 +156,7 @@ class Job(models.Model):
)

def __str__(self):
return f"<Job {self.pk} | {self.status}>"
return f"<Job {self.id} | {self.status}>"

def in_terminal_state(self):
"""Returns true if job is in terminal state."""
Expand Down
75 changes: 73 additions & 2 deletions gateway/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@
Version serializers inherit from the different serializers.
"""

import json
import logging
from django.conf import settings
from rest_framework import serializers

from api.utils import build_env_variables, encrypt_env_vars
from .models import Program, Job, JobConfig, RuntimeJob

logger = logging.getLogger("gateway.serializers")


class UploadProgramSerializer(serializers.ModelSerializer):
"""
Expand All @@ -30,9 +36,14 @@ def retrieve_one_by_title(self, title, author):
)

def create(self, validated_data):
title = validated_data.get("title")
logger.info("Creating program [%s] with UploadProgramSerializer", title)
return Program.objects.create(**validated_data)

def update(self, instance, validated_data):
logger.info(
"Updating program [%s] with UploadProgramSerializer", instance.title
)
instance.arguments = validated_data.get("arguments", "{}")
instance.entrypoint = validated_data.get("entrypoint")
instance.dependencies = validated_data.get("dependencies", "[]")
Expand Down Expand Up @@ -98,11 +109,32 @@ class Meta:
model = Job


class ExistingProgramSerializer(serializers.Serializer):
"""Serializer for launching existing program."""
class RunExistingProgramSerializer(serializers.Serializer):
"""
Program serializer for the /run_existing end-point
"""

title = serializers.CharField(max_length=255)
arguments = serializers.CharField()
config = serializers.CharField()

def retrieve_one_by_title(self, title, author):
"""
This method returns a Program entry if it finds an entry searching by the title, if not None
"""
return (
Program.objects.filter(title=title, author=author)
.order_by("-created")
.first()
)

def to_representation(self, instance):
"""
Transforms string `config` to JSON
"""
representation = super().to_representation(instance)
representation["config"] = json.loads(representation["config"])
return representation

def update(self, instance, validated_data):
pass
Expand All @@ -111,6 +143,45 @@ def create(self, validated_data):
pass


class RunExistingJobSerializer(serializers.ModelSerializer):
"""
Job serializer for the /run_existing end-point
"""

class Meta:
model = Job

def create(self, validated_data):
logger.info("Creating Job with RunExistingJobSerializer")
status = Job.QUEUED
program = validated_data.get("program")
arguments = validated_data.get("arguments", "{}")
author = validated_data.get("author")
config = validated_data.get("config", None)

token = validated_data.pop("token")
carrier = validated_data.pop("carrier")

job = Job(
status=status,
program=program,
arguments=arguments,
author=author,
config=config,
)

env = encrypt_env_vars(build_env_variables(token, job, arguments))
try:
env["traceparent"] = carrier["traceparent"]
except KeyError:
pass

job.env_vars = json.dumps(env)
job.save()

return job


class RuntimeJobSerializer(serializers.ModelSerializer):
"""
Serializer for the runtime job model.
Expand Down
2 changes: 2 additions & 0 deletions gateway/api/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"""

# pylint: disable=too-few-public-methods
# pylint: disable=duplicate-code
# Disable duplicate code due to refactorization. This file will be delited.

import logging
import json
Expand Down
4 changes: 2 additions & 2 deletions gateway/api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import re
import time
import uuid
from typing import Optional, Tuple, Union, Callable, Dict, Any
from typing import Optional, Tuple, Union, Callable, Dict

from cryptography.fernet import Fernet
from ray.dashboard.modules.job.common import JobStatus
Expand Down Expand Up @@ -106,7 +106,7 @@ def decrypt_string(string: str) -> str:
return fernet.decrypt(string.encode("utf-8")).decode("utf-8")


def build_env_variables(token, job: Job, arguments: Dict[str, Any]) -> Dict[str, str]:
def build_env_variables(token, job: Job, arguments: str) -> Dict[str, str]:
"""Builds env variables for job.
Args:
Expand Down
35 changes: 22 additions & 13 deletions gateway/api/v1/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@ class Meta(serializers.ProgramSerializer.Meta):
]


class ExistingProgramSerializer(serializers.ExistingProgramSerializer):
"""
Existing program serializer first version. This serializer limitates the fields from Program.
"""


class UploadProgramSerializer(serializers.UploadProgramSerializer):
"""
UploadProgramSerializer is used by the /upload end-point
Expand All @@ -41,16 +35,11 @@ class Meta(serializers.UploadProgramSerializer.Meta):
]


class JobSerializer(serializers.JobSerializer):
class RunExistingProgramSerializer(serializers.RunExistingProgramSerializer):
"""
Job serializer first version. Include basic fields from the initial model.
RunExistingProgramSerializer is used by the /upload end-point
"""

program = ProgramSerializer(many=False)

class Meta(serializers.JobSerializer.Meta):
fields = ["id", "result", "status", "program", "created"]


class JobConfigSerializer(serializers.JobConfigSerializer):
"""
Expand All @@ -67,6 +56,26 @@ class Meta(serializers.JobConfigSerializer.Meta):
]


class RunExistingJobSerializer(serializers.RunExistingJobSerializer):
"""
RunExistingJobSerializer is used by the /run_existing end-point
"""

class Meta(serializers.RunExistingJobSerializer.Meta):
fields = ["id", "result", "status", "program", "created", "arguments"]


class JobSerializer(serializers.JobSerializer):
"""
Job serializer first version. Include basic fields from the initial model.
"""

program = ProgramSerializer(many=False)

class Meta(serializers.JobSerializer.Meta):
fields = ["id", "result", "status", "program", "created"]


class RuntimeJobSerializer(serializers.RuntimeJobSerializer):
"""
Runtime job serializer first version. Serializer for the runtime job model.
Expand Down
29 changes: 21 additions & 8 deletions gateway/api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,24 @@ def get_service_job_class():
return v1_services.JobService

@staticmethod
def get_serializer_job_class():
return v1_serializers.JobSerializer
def get_serializer_job(*args, **kwargs):
return v1_serializers.JobSerializer(*args, **kwargs)

@staticmethod
def get_serializer_existing_program_class():
return v1_serializers.ExistingProgramSerializer
def get_serializer_job_config(*args, **kwargs):
return v1_serializers.JobConfigSerializer(*args, **kwargs)

@staticmethod
def get_serializer_job_config_class():
return v1_serializers.JobConfigSerializer
def get_serializer_upload_program(*args, **kwargs):
return v1_serializers.UploadProgramSerializer(*args, **kwargs)

@staticmethod
def get_serializer_upload_program_class(*args, **kwargs):
return v1_serializers.UploadProgramSerializer(*args, **kwargs)
def get_serializer_run_existing_program(*args, **kwargs):
return v1_serializers.RunExistingProgramSerializer(*args, **kwargs)

@staticmethod
def get_serializer_run_existing_job(*args, **kwargs):
return v1_serializers.RunExistingJobSerializer(*args, **kwargs)

def get_serializer_class(self):
return v1_serializers.ProgramSerializer
Expand All @@ -63,6 +67,15 @@ def get_serializer_class(self):
def upload(self, request):
return super().upload(request)

@swagger_auto_schema(
operation_description="Run an existing Qiskit Pattern",
request_body=v1_serializers.RunExistingProgramSerializer,
responses={status.HTTP_200_OK: v1_serializers.RunExistingJobSerializer},
)
@action(methods=["POST"], detail=False)
def run_existing(self, request):
return super().run_existing(request)


class JobViewSet(views.JobViewSet): # pylint: disable=too-many-ancestors
"""
Expand Down
Loading

0 comments on commit 10f6b19

Please sign in to comment.