Skip to content

Commit

Permalink
Configure tasks to be able to run configurations (#1407)
Browse files Browse the repository at this point in the history
* Create tasks for migrations

* Added tests for the tasks

* Fix linter

* Add support for tasks configurations in helm

* Set correctly the type to string

* Catch json loads exceptions
  • Loading branch information
Tansito authored Jul 12, 2024
1 parent 59bc558 commit feb4c67
Show file tree
Hide file tree
Showing 10 changed files with 222 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ spec:
secretKeyRef:
name: {{ .Values.secrets.servicePsql.name }}
key: {{ .Values.secrets.servicePsql.key.databasePassword }}
- name: PROVIDERS_CONFIGURATION
value: "{{ .Values.tasks.providersConfiguration }}"
- name: FUNCTIONS_PERMISSIONS
value: "{{ .Values.tasks.functionsPermissions }}"
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
Expand Down
4 changes: 4 additions & 0 deletions charts/qiskit-serverless/charts/gateway/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ application:
cos:
claimName: gateway-claim

tasks:
providersConfiguration: '{}'
functionsPermissions: '{}'

secrets:
secretKey:
create: true
Expand Down
14 changes: 14 additions & 0 deletions gateway/api/apps.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
"""Applications."""

from django.apps import AppConfig
from django.db.models.signals import post_migrate


class ApiConfig(AppConfig):
"""ApiConfig."""

default_auto_field = "django.db.models.BigAutoField"
name = "api"

def ready(self):
def on_migrations_applied(**kwargs):
# This import may be here for the correct initialization of the App
from api.tasks import ( # pylint: disable=import-outside-toplevel
programs,
providers,
)

providers.assign_admin_group()
programs.assign_run_permission()

post_migrate.connect(on_migrations_applied)
Empty file added gateway/api/tasks/__init__.py
Empty file.
67 changes: 67 additions & 0 deletions gateway/api/tasks/programs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""
This file contains the main tasks to manage programs
"""

import json
import logging
from django.conf import settings
from django.contrib.auth.models import Group, Permission

from api.models import RUN_PROGRAM_PERMISSION, Program, Provider


logger = logging.getLogger("gateway")


def assign_run_permission():
"""
This method assigns the run permission to a group and
assigns that group to a specific program.
"""
try:
functions_permissions = json.loads(settings.FUNCTIONS_PERMISSIONS)
except json.JSONDecodeError as e:
logger.error("Assign run permission JSON malformed: %s", e)
return
except Exception as e: # pylint: disable=broad-exception-caught
logger.error("Assign run permission unexpected error: %s", e)
return

for function_title, function_info in functions_permissions.items():
provider_name = function_info["provider"]
instances_titles = function_info["instances"]

program = None
provider = Provider.objects.filter(name=provider_name).first()
if provider is None:
logger.warning("Provider [%s] does not exist", provider_name)
else:
program = Program.objects.filter(
title=function_title, provider=provider
).first()

if program is None:
logger.warning(
"Program [%s] does not exist for Provider [%s]",
function_title,
provider_name,
)
else:
run_permission = Permission.objects.get(codename=RUN_PROGRAM_PERMISSION)

for instance_title in instances_titles:
groups = []
group = Group.objects.filter(name=instance_title).first()
if group is None:
logger.warning("Group [%s] does not exist", instance_title)
else:
logger.info("Group [%s] does not exist", instance_title)
group.permissions.add(run_permission)
groups.append(group)

logger.info(
"Program [%s] is going to be updated with [%s] groups",
program.title,
len(groups),
)
program.instances.set(groups)
44 changes: 44 additions & 0 deletions gateway/api/tasks/providers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""
This file contains the main tasks to configure the providers
"""

import json
import logging
from django.conf import settings
from django.contrib.auth.models import Group

from api.models import Provider


logger = logging.getLogger("gateway")


def assign_admin_group():
"""
This method will assign a group to a provider.
If the provider does not exist it will be created.
"""
try:
providers_configuration = json.loads(settings.PROVIDERS_CONFIGURATION)
except json.JSONDecodeError as e:
logger.error("Assign admin group JSON malformed: %s", e)
return
except Exception as e: # pylint: disable=broad-exception-caught
logger.error("Assign admin group unexpected error: %s", e)
return

for provider_name, admin_group_name in providers_configuration.items():
group = Group.objects.filter(name=admin_group_name).first()
if group is None:
logger.warning("Group [%s] does not exist", admin_group_name)
else:
provider, created = Provider.objects.update_or_create(
name=provider_name, defaults={"admin_group": group}
)

if created:
logger.info(
"Provider [%s] created for admin [%s]",
provider.name,
admin_group_name,
)
9 changes: 9 additions & 0 deletions gateway/main/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,3 +378,12 @@
CUSTOM_IMAGE_PACKAGE_PATH = os.environ.get("CUSTOM_IMAGE_PACKAGE_PATH", "/runner")
SECURE_BROWSER_XSS_FILTER = True
SESSION_COOKIE_AGE = 3600

# Providers setup
PROVIDERS_CONFIGURATION = os.environ.get("PROVIDERS_CONFIGURATION", "{}")

# Function permissions
FUNCTIONS_PERMISSIONS = os.environ.get(
"FUNCTIONS_PERMISSIONS",
"{}",
)
Empty file.
56 changes: 56 additions & 0 deletions gateway/tests/api/tasks/test_tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from django.contrib.auth import models
from rest_framework.test import APITestCase, override_settings
from rest_framework import status

from api.models import RUN_PROGRAM_PERMISSION, Provider, Program
from api.tasks import programs, providers


class TestProgramApi(APITestCase):
"""TestProgramApi."""

fixtures = ["tests/fixtures/tasks_fixtures.json"]

@override_settings(PROVIDERS_CONFIGURATION='{"test_provider": "runner"}')
def test_assign_admin_group(self):
"""This test will check assign admin group task"""

providers.assign_admin_group()

provider = Provider.objects.get(name="test_provider")
self.assertEqual(provider.admin_group.name, "runner")

@override_settings(PROVIDERS_CONFIGURATION='{"test_provider": "runner"}')
@override_settings(
FUNCTIONS_PERMISSIONS='{"function_provider": {"provider": "test_provider", "instances": ["runner"]}}'
)
def test_assign_run_permission(self):
providers.assign_admin_group()

user = models.User.objects.get(username="test_user")
self.client.force_authenticate(user=user)

programs_response = self.client.post(
"/api/v1/programs/upload/",
data={
"title": "function_provider",
"dependencies": "[]",
"env_vars": "{}",
"image": "icr.io/awesome-namespace/awesome-title",
"provider": "test_provider",
},
)
print(programs_response.content)
self.assertEqual(programs_response.status_code, status.HTTP_200_OK)

programs.assign_run_permission()

program = Program.objects.get(title="function_provider")
self.assertEqual(len(program.instances.all()), 1)
self.assertEqual(program.instances.all()[0].name, "runner")

group = program.instances.all()[0]
self.assertEqual(len(group.permissions.all()), 2)

run_permission = models.Permission.objects.get(codename=RUN_PROGRAM_PERMISSION)
self.assertTrue(group.permissions.filter(id=run_permission.pk).exists())
24 changes: 24 additions & 0 deletions gateway/tests/fixtures/tasks_fixtures.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[
{
"model": "auth.user",
"pk": 1,
"fields": {
"email": "[email protected]",
"username": "test_user",
"password": "pbkdf2_sha256$390000$kcex1rxhZg6VVJYkx71cBX$e4ns0xDykbO6Dz6j4nZ4uNusqkB9GVpojyegPv5/9KM=",
"is_active": true,
"groups": [
100
]
}
},
{
"model": "auth.group",
"pk": 100,
"fields": {
"name": "runner",
"permissions": [60]
}
}
]

0 comments on commit feb4c67

Please sign in to comment.