From d8c2aae22300414a50f9b54b62b62705dce12a2e Mon Sep 17 00:00:00 2001 From: Tobias Kunze Date: Thu, 4 Sep 2025 01:17:24 +0200 Subject: [PATCH] Remove ServiceInstance soft-delete --- src/servala/core/admin.py | 7 ++--- ...006_remove_service_instance_soft_delete.py | 25 ++++++++++++++++ src/servala/core/models/service.py | 29 +++---------------- src/servala/frontend/forms/service.py | 13 --------- .../frontend/organizations/dashboard.html | 7 ----- .../service_instance_detail.html | 23 +++------------ .../organizations/service_instances.html | 10 +------ src/servala/frontend/views/generic.py | 6 ++-- src/servala/frontend/views/organization.py | 4 +-- src/servala/frontend/views/service.py | 23 ++------------- 10 files changed, 42 insertions(+), 105 deletions(-) create mode 100644 src/servala/core/migrations/0006_remove_service_instance_soft_delete.py diff --git a/src/servala/core/admin.py b/src/servala/core/admin.py index ed07574..149635c 100644 --- a/src/servala/core/admin.py +++ b/src/servala/core/admin.py @@ -272,8 +272,8 @@ class ControlPlaneCRDAdmin(admin.ModelAdmin): @admin.register(ServiceInstance) class ServiceInstanceAdmin(admin.ModelAdmin): - list_display = ("name", "organization", "context", "created_by", "is_deleted") - list_filter = ("organization", "context", "is_deleted") + list_display = ("name", "organization", "context", "created_by") + list_filter = ("organization", "context") search_fields = ( "name", "organization__name", @@ -296,9 +296,6 @@ class ServiceInstanceAdmin(admin.ModelAdmin): "organization", "context", "created_by", - "is_deleted", - "deleted_at", - "deleted_by", ) }, ), diff --git a/src/servala/core/migrations/0006_remove_service_instance_soft_delete.py b/src/servala/core/migrations/0006_remove_service_instance_soft_delete.py new file mode 100644 index 0000000..0fcc02a --- /dev/null +++ b/src/servala/core/migrations/0006_remove_service_instance_soft_delete.py @@ -0,0 +1,25 @@ +# Generated by Django 5.2.4 on 2025-09-03 23:08 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("core", "0005_organization_sale_order_fields"), + ] + + operations = [ + migrations.RemoveField( + model_name="serviceinstance", + name="deleted_at", + ), + migrations.RemoveField( + model_name="serviceinstance", + name="deleted_by", + ), + migrations.RemoveField( + model_name="serviceinstance", + name="is_deleted", + ), + ] diff --git a/src/servala/core/models/service.py b/src/servala/core/models/service.py index a06db4e..3aceedd 100644 --- a/src/servala/core/models/service.py +++ b/src/servala/core/models/service.py @@ -10,7 +10,6 @@ from django.conf import settings from django.core.cache import cache from django.core.exceptions import ValidationError from django.db import IntegrityError, models, transaction -from django.utils import timezone from django.utils.functional import cached_property from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ @@ -571,16 +570,6 @@ class ServiceInstance(ServalaModelMixin, models.Model): on_delete=models.PROTECT, ) - is_deleted = models.BooleanField(default=False) - deleted_at = models.DateTimeField(null=True, blank=True) - deleted_by = models.ForeignKey( - to="core.User", - on_delete=models.SET_NULL, - null=True, - blank=True, - related_name="+", - ) - class Meta: verbose_name = _("Service instance") verbose_name_plural = _("Service instances") @@ -794,14 +783,10 @@ class ServiceInstance(ServalaModelMixin, models.Model): raise ValidationError(self.organization.add_support_message(message)) @transaction.atomic - def delete_instance(self, user): + def delete(self, using=None, keep_parents=False, user=None): """ - Soft deletes the instance in Django and initiates deletion of the - corresponding Kubernetes custom resource. + Deletes the Django instance and the corresponding Kubernetes custom resource. """ - if self.is_deleted: - return - if ( self.spec.get("parameters", {}) .get("security", {}) @@ -825,19 +810,13 @@ class ServiceInstance(ServalaModelMixin, models.Model): if e.status != 404: # 404 is fine, the object was deleted already. raise - self.is_deleted = True - self.deleted_at = timezone.now() - self.deleted_by = user - self.save( - update_fields=["is_deleted", "deleted_at", "deleted_by", "updated_at"] - ) + self._clear_kubernetes_caches() + return super().delete(using=using, keep_parents=keep_parents) @cached_property def kubernetes_object(self): """Fetch the Kubernetes custom resource object""" - if self.is_deleted: - return try: api_instance = client.CustomObjectsApi( self.context.control_plane.get_kubernetes_client() diff --git a/src/servala/frontend/forms/service.py b/src/servala/frontend/forms/service.py index ca84ab3..5dd78a7 100644 --- a/src/servala/frontend/forms/service.py +++ b/src/servala/frontend/forms/service.py @@ -64,14 +64,6 @@ class ServiceInstanceFilterForm(forms.Form): required=False, label=_("Service Provider Zone"), ) - status = forms.ChoiceField( - choices=( - ("active", _("Active")), - ("deleted", _("Deleted")), - ), - required=False, - label=_("Status"), - ) def filter_queryset(self, queryset): if self.is_valid(): @@ -88,11 +80,6 @@ class ServiceInstanceFilterForm(forms.Form): ) if data.get("control_plane"): queryset = queryset.filter(context__control_plane=data["control_plane"]) - status = data.get("status") - if status == "active": - queryset = queryset.filter(is_deleted=False) - elif status == "deleted": - queryset = queryset.filter(is_deleted=True) return queryset diff --git a/src/servala/frontend/templates/frontend/organizations/dashboard.html b/src/servala/frontend/templates/frontend/organizations/dashboard.html index b0dd273..01faa69 100644 --- a/src/servala/frontend/templates/frontend/organizations/dashboard.html +++ b/src/servala/frontend/templates/frontend/organizations/dashboard.html @@ -112,13 +112,6 @@ {{ instance.context.service_offering.service.name }} - - {% if instance.is_deleted %} - {% translate "Deleted" %} - {% else %} - {% translate "Active" %} - {% endif %} - {{ instance.created_at|date:"M d, Y" }} diff --git a/src/servala/frontend/templates/frontend/organizations/service_instance_detail.html b/src/servala/frontend/templates/frontend/organizations/service_instance_detail.html index 4c8ebf1..a12efcf 100644 --- a/src/servala/frontend/templates/frontend/organizations/service_instance_detail.html +++ b/src/servala/frontend/templates/frontend/organizations/service_instance_detail.html @@ -7,10 +7,10 @@ {% endblock html_title %} {% block page_title_extra %}
- {% if has_change_permission and not instance.is_deleted %} + {% if has_change_permission %} {% translate "Edit" %} {% endif %} - {% if has_delete_permission and not instance.is_deleted %} + {% if has_delete_permission %}
- {% if not instance.is_deleted and instance.status_conditions %} + {% if instance.status_conditions %}
@@ -118,7 +103,7 @@
{% endif %} - {% if not instance.is_deleted and instance.spec and spec_fieldsets %} + {% if instance.spec and spec_fieldsets %}
diff --git a/src/servala/frontend/templates/frontend/organizations/service_instances.html b/src/servala/frontend/templates/frontend/organizations/service_instances.html index e5c7c0e..c443e6e 100644 --- a/src/servala/frontend/templates/frontend/organizations/service_instances.html +++ b/src/servala/frontend/templates/frontend/organizations/service_instances.html @@ -27,7 +27,6 @@ {% translate "Service Provider" %} {% translate "Service Provider Zone" %} {% translate "Created At" %} - {% translate "Status" %} @@ -40,17 +39,10 @@ {{ instance.context.service_offering.provider.name }} {{ instance.context.control_plane.name }} {{ instance.created_at|date:"SHORT_DATETIME_FORMAT" }} - - {% if instance.is_deleted %} - {% translate "Deleted" %} - {% else %} - {% translate "Active" %} - {% endif %} - {% empty %} - {% translate "No service instances found." %} + {% translate "No service instances found." %} {% endfor %} diff --git a/src/servala/frontend/views/generic.py b/src/servala/frontend/views/generic.py index 16b1ec2..170b159 100644 --- a/src/servala/frontend/views/generic.py +++ b/src/servala/frontend/views/generic.py @@ -1,6 +1,6 @@ from django.conf import settings from django.contrib.auth.mixins import LoginRequiredMixin -from django.db.models import Count, Q +from django.db.models import Count from django.shortcuts import redirect, render from django.urls import reverse_lazy from django.utils.functional import cached_property @@ -32,9 +32,7 @@ class OrganizationSelectionView(LoginRequiredMixin, TemplateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["user_organizations"] = self.user_organizations.annotate( - instance_count=Count( - "service_instances", filter=Q(service_instances__is_deleted=False) - ) + instance_count=Count("service_instances") ) return context diff --git a/src/servala/frontend/views/organization.py b/src/servala/frontend/views/organization.py index 6545d39..2f35f76 100644 --- a/src/servala/frontend/views/organization.py +++ b/src/servala/frontend/views/organization.py @@ -70,9 +70,7 @@ class OrganizationDashboardView( context = super().get_context_data(**kwargs) organization = self.get_object() - service_instances = ServiceInstance.objects.filter( - organization=organization, is_deleted=False - ) + service_instances = ServiceInstance.objects.filter(organization=organization) recent_instances = service_instances.order_by("-created_at")[:5] for instance in recent_instances: diff --git a/src/servala/frontend/views/service.py b/src/servala/frontend/views/service.py index f9ce50d..201a510 100644 --- a/src/servala/frontend/views/service.py +++ b/src/servala/frontend/views/service.py @@ -198,11 +198,7 @@ class ServiceInstanceMixin: def get_object(self, **kwargs): instance = super().get_object(**kwargs) - if ( - not instance.is_deleted - and not instance.kubernetes_object - and not self._has_warned - ): + if not instance.kubernetes_object and not self._has_warned: messages.warning( self.request, _( @@ -221,11 +217,7 @@ class ServiceInstanceDetailView( def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - if ( - not self.object.is_deleted - and self.object.kubernetes_object - and self.object.spec - ): + if self.object.kubernetes_object and self.object.spec: context["spec_fieldsets"] = self.get_nested_spec() context["has_change_permission"] = self.request.user.has_perm( ServiceInstance.get_perm("change"), self.object @@ -406,15 +398,6 @@ class ServiceInstanceListView(OrganizationViewMixin, ListView): ) if self.filter_form.is_valid(): queryset = self.filter_form.filter_queryset(queryset) - status_filter = ( - self.filter_form.cleaned_data.get("status") - if self.filter_form.is_valid() - else "active" - ) - if status_filter == "active": - queryset = queryset.filter(is_deleted=False) - elif status_filter == "deleted": - queryset = queryset.filter(is_deleted=True) return queryset.order_by("-created_at") def get_context_data(self, **kwargs): @@ -433,7 +416,7 @@ class ServiceInstanceDeleteView( def form_valid(self, form): try: - self.object.delete_instance(user=self.request.user) + self.object.delete(user=self.request.user) messages.success( self.request, _("Service instance '{name}' has been scheduled for deletion.").format(