Skip to content
This repository has been archived by the owner on Mar 17, 2024. It is now read-only.

Commit

Permalink
Merge pull request #317 from scaleoutsystems/feature/subtree_pull
Browse files Browse the repository at this point in the history
Pull in changes from git subtrees
  • Loading branch information
Wrede committed Mar 21, 2023
2 parents 1d933b6 + 691fc80 commit 254bbaf
Show file tree
Hide file tree
Showing 41 changed files with 1,738 additions and 894 deletions.
43 changes: 18 additions & 25 deletions api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from django.conf import settings
from django.contrib.auth.models import User
from django.db.models import Q
from django.http import HttpRequest, HttpResponse
from django.http import HttpResponse
from django.utils.text import slugify
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import generics
Expand Down Expand Up @@ -508,43 +508,36 @@ def create(self, request, *args, **kwargs):
app_slug = request.data["slug"]
data = request.data
user = request.user
import apps.views as appviews

request = HttpRequest()
request.user = user
create_view = appviews.CreateView()
_ = create_view.post(
request,
user=user.username,
import apps.helpers as helpers

app = Apps.objects.filter(slug=app_slug).order_by("-revision")[0]

(successful, _, _,) = helpers.create_app_instance(
user=user,
project=project,
app=app,
app_settings=app.settings,
data=data,
project=project.slug,
app_slug=app_slug,
wait=True,
call=True,
)

if not successful:
print("create_app_instance failed")
return HttpResponse("App creation faild", status=400)

return HttpResponse("App created.", status=200)

def destroy(self, request, *args, **kwargs):
project = Project.objects.get(id=self.kwargs["project_pk"])
appinstance = self.get_object()
# Check that user is allowed to delete app:
# Either user owns the app, or is a member of the project
# (Checked by project permission above)
# and the app is set to project level permission.
access = False
if appinstance.owner == request.user:
print("User owns app, can delete.")
access = True
elif appinstance.permission.projects.filter(
slug=project.slug
).exists():
print("Project has permission")
access = True
elif appinstance.permission.public:
print(
"Public app and user has project permission, delete granted."
)

if appinstance.access == "public":
access = True

if access:
delete_resource.delay(appinstance.pk)
else:
Expand Down
18 changes: 8 additions & 10 deletions apps/admin.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
from django.contrib import admin

from .models import (
AppCategories,
AppInstance,
AppPermission,
Apps,
AppStatus,
ResourceData,
)
from .models import AppCategories, AppInstance, Apps, AppStatus, ResourceData

admin.site.register(Apps)

class AppsAdmin(admin.ModelAdmin):
list_display = ("name", "user_can_create", "slug", "revision")
list_filter = ("user_can_create",)


admin.site.register(Apps, AppsAdmin)
admin.site.register(AppInstance)
admin.site.register(AppCategories)
admin.site.register(AppPermission)
admin.site.register(ResourceData)
admin.site.register(AppStatus)
22 changes: 22 additions & 0 deletions apps/helpers/get_apps_limit_per_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from django.conf import settings


def get_apps_limit_per_user(slug):
"""get_apps_limit_per_user
Args:
slug (App.slug): slug for the app type
Returns:
Integer or None: returns the limit or None if not set
"""
try:
apps_per_user_limit = (
settings.APPS_PER_USER_LIMIT
if settings.APPS_PER_USER_LIMIT is not None
else {}
)
except Exception:
apps_per_user_limit = {}

return apps_per_user_limit[slug] if slug in apps_per_user_limit else None
87 changes: 87 additions & 0 deletions apps/helpers.py → apps/helpers/helpers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import time
import uuid

from django.apps import apps
from django.conf import settings
from django.template import engines

from ..models import AppInstance, AppStatus
from ..serialize import serialize_app
from ..tasks import deploy_resource

ReleaseName = apps.get_model(app_label=settings.RELEASENAME_MODEL)


def create_instance_params(instance, action="create"):
Expand Down Expand Up @@ -113,3 +122,81 @@ def handle_permissions(parameters, project):
access = "private"

return access


def create_app_instance(user, project, app, app_settings, data=[], wait=False):
app_name = data.get("app_name")

parameters_out, app_deps, model_deps = serialize_app(
data, project, app_settings, user.username
)

authorized = can_access_app_instances(app_deps, user, project)

if not authorized:
raise Exception("Not authorized to use specified app dependency")

access = handle_permissions(parameters_out, project)

app_instance = AppInstance(
name=app_name,
access=access,
app=app,
project=project,
info={},
parameters=parameters_out,
owner=user,
)

create_instance_params(app_instance, "create")

# Attempt to create a ReleaseName model object
rel_name_obj = []
if "app_release_name" in data and data.get("app_release_name") != "":
submitted_rn = data.get("app_release_name")
try:
rel_name_obj = ReleaseName.objects.get(
name=submitted_rn, project=project, status="active"
)
rel_name_obj.status = "in-use"
rel_name_obj.save()
app_instance.parameters["release"] = submitted_rn
except Exception as e:
print("Error: Submitted release name not owned by project.")
print(e)
return [False, None, None]

# Add fields for apps table:
# to be displayed as app details in views
if app_instance.app.table_field and app_instance.app.table_field != "":
django_engine = engines["django"]
info_field = django_engine.from_string(
app_instance.app.table_field
).render(app_instance.parameters)
app_instance.table_field = eval(info_field)
else:
app_instance.table_field = {}

# Setting status fields before saving app instance
status = AppStatus(appinstance=app_instance)
status.status_type = "Created"
status.info = app_instance.parameters["release"]
app_instance.save()
# Saving ReleaseName, permissions, status and
# setting up dependencies
if rel_name_obj:
rel_name_obj.app = app_instance
rel_name_obj.save()
status.save()
app_instance.app_dependencies.set(app_deps)
app_instance.model_dependencies.set(model_deps)

# Finally, attempting to create apps resources
res = deploy_resource.delay(app_instance.pk, "create")

# wait is passed as a function parameter
if wait:
while not res.ready():
time.sleep(0.1)

return [True, project.slug, app_instance.app.category.slug]
16 changes: 16 additions & 0 deletions apps/migrations/0003_delete_apppermission.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Generated by Django 4.1.7 on 2023-03-14 13:26

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('apps', '0002_initial'),
]

operations = [
migrations.DeleteModel(
name='AppPermission',
),
]
32 changes: 17 additions & 15 deletions apps/models.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,12 @@
from django.contrib.auth import get_user_model
from django.db import models
from django.db.models import Q
from django.db.models.signals import post_save
from django.dispatch import receiver
from guardian.shortcuts import assign_perm, remove_perm
from tagulous.models import TagField


class AppPermission(models.Model):
appinstance = models.OneToOneField(
"apps.AppInstance",
on_delete=models.CASCADE,
null=True,
related_name="permission",
)
name = models.CharField(max_length=512, default="permission_name")
projects = models.ManyToManyField("projects.Project")
public = models.BooleanField(default=False)
users = models.ManyToManyField(get_user_model())

def __str__(self):
return str(self.name)
from apps.helpers.get_apps_limit_per_user import get_apps_limit_per_user


class AppCategories(models.Model):
Expand Down Expand Up @@ -69,7 +56,22 @@ def __str__(self):
return str(self.name) + "({})".format(self.revision)


class AppInstanceManager(models.Manager):
def user_can_create(self, user, project, app_slug):
limit = get_apps_limit_per_user(app_slug)

num_of_app_instances = self.filter(
Q(owner=user), app__slug=app_slug, project=project
).count()

has_perm = user.has_perm("apps.add_appinstance")

return limit is None or limit > num_of_app_instances or has_perm


class AppInstance(models.Model):
objects = AppInstanceManager()

access = models.CharField(
max_length=20, default="private", null=True, blank=True
)
Expand Down
4 changes: 1 addition & 3 deletions apps/serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,10 +311,8 @@ def serialize_env_variables(username, project, aset):
print("fetching apps")
try:
apps = AppInstance.objects.filter(
Q(owner__username=username)
| Q(permission__projects__slug=project.slug)
| Q(permission__public=True),
~Q(state="Deleted"),
Q(owner__username=username) | Q(access__in=["project", "public"]),
project=project,
)
except Exception as err:
Expand Down
2 changes: 1 addition & 1 deletion apps/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ def sync_mlflow_models():
for mlflow_app in mlflow_apps:
url = "http://{}/{}".format(
mlflow_app.project.mlflow.host,
"api/2.0/preview/mlflow/model-versions/search",
"api/2.0/mlflow/model-versions/search",
)
res = False
try:
Expand Down
17 changes: 11 additions & 6 deletions apps/templates/app_table.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,31 +34,36 @@
<td class="table-action">
<div class="dropdown show">
<a href="#" data-bs-toggle="dropdown" data-display="static">
<i class="fas fa-ellipsis-v"></i>
<i class="bi bi-three-dots-vertical"></i>
</a>
<div class="dropdown-menu dropdown-menu-end">
<a class="dropdown-item disabled" href="{% url 'apps:logs' request.user project.slug appinstance.pk %}">
<i class="align-middle me-1" data-feather="activity"></i> Logs (disabled)
<i class="bi bi-activity me-1"></i>
Logs (disabled)
</a>

{% if appinstance.app.settings.publishable == "true" and appinstance.owner.id == request.user.id %}
{% if appinstance.access == "public" %}
<a class="dropdown-item" href="{% url 'apps:unpublish' request.user project.slug category appinstance.pk %}">
<i class="align-middle me-1" data-feather="slash"></i> Unpublish
<i class="bi bi-slash-circle me-1"></i>
Unpublish
</a>
{% else %}
<a class="dropdown-item" href="{% url 'apps:publish' request.user project.slug category appinstance.pk %}">
<i class="align-middle me-1" data-feather="share-2"></i> Publish
<i class="bi bi-share me-1"></i>
Publish
</a>
{% endif %}
{% endif %}

<a class="dropdown-item" href="{% url 'apps:appsettings' request.user project.slug appinstance.pk %}">
<i class="align-middle me-1" data-feather="sliders"></i> Settings
<i class="bi bi-sliders2-vertical me-1"></i>
Settings
</a>

<a class="dropdown-item bg-danger text-white confirm-delete" href="{% url 'apps:delete' request.user project.slug category appinstance.pk %}">
<i class="align-middle me-1" data-feather="trash"></i> Delete
<i class="bi bi-trash me-1"></i>
Delete
</a>
</div>
</div>
Expand Down
12 changes: 1 addition & 11 deletions apps/templates/create.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<h1 class="h3 mb-3">Create {{ app.name }}</h1>
<div class="row">
<div class="col-12 col-xl-6">
<div class="card">
<div class="card shadow border-0">
<div class="card-body">
<form action="{% url 'apps:create' request.user project.slug app.slug %}?from={{ from_page }}" method="post">
{% csrf_token %}
Expand All @@ -18,16 +18,6 @@ <h1 class="h3 mb-3">Create {{ app.name }}</h1>
<input type="text" id="app_name" name="app_name" value="{{ existing_app_name }}" class="form-control" required>
</div>

<div class="mb-3">
<label class="form-label">Subdomain</label>
<select name="app_release_name" id="app_release_name" class="form-control">
<option value="" selected>---- Generated ----</option>
{% for item in form.release_names %}
<option value="{{ item.name }}">{{ item.name }}</option>
{% endfor %}
</select>
</div>

{% if form.dep_permissions %}
<div class="mb-3">
<label class="form-label">Permissions</label>
Expand Down

0 comments on commit 254bbaf

Please sign in to comment.