diff --git a/src/servala/core/models/organization.py b/src/servala/core/models/organization.py
index da8c11b..f1ddf26 100644
--- a/src/servala/core/models/organization.py
+++ b/src/servala/core/models/organization.py
@@ -79,7 +79,9 @@ class Organization(ServalaModelMixin, models.Model):
support_message = _(
"Need help? We're happy to help via the support form."
).format(support_url=self.urls.support)
- return mark_safe(f"{message} {support_message}")
+ return mark_safe(
+ f'{message} {support_message}'
+ )
@classmethod
@transaction.atomic
diff --git a/src/servala/core/models/service.py b/src/servala/core/models/service.py
index 365fcce..b4d27b3 100644
--- a/src/servala/core/models/service.py
+++ b/src/servala/core/models/service.py
@@ -1,5 +1,6 @@
import copy
import json
+import re
import kubernetes
import rules
@@ -604,6 +605,32 @@ class ServiceInstance(ServalaModelMixin, models.Model):
spec_data = prune_empty_data(spec_data)
return spec_data
+ @classmethod
+ def _format_kubernetes_error(cls, error_message):
+ """
+ Format Kubernetes API error messages for better user experience.
+ Converts validation error arrays into unordered lists.
+ """
+ # Pattern to match validation errors in brackets
+ pattern = r"\[([^\]]+)\]"
+ match = re.search(pattern, error_message)
+
+ if not match:
+ return error_message
+
+ errors_text = match.group(1)
+ # Split by comma and clean up each error
+ errors = [error.strip() for error in errors_text.split(",")]
+
+ if len(errors) > 1:
+ # Format as HTML unordered list
+ error_list = "".join(f"
{error}" for error in errors)
+ # Replace the bracketed section with the formatted list
+ formatted_message = re.sub(pattern, f"", error_message)
+ return mark_safe(formatted_message)
+
+ return error_message
+
@classmethod
def create_instance(cls, name, organization, context, created_by, spec_data):
# Ensure the namespace exists
@@ -616,7 +643,9 @@ class ServiceInstance(ServalaModelMixin, models.Model):
context=context,
)
except IntegrityError:
- message = "An instance with this name already exists in this organization. Please choose a different name."
+ message = _(
+ "An instance with this name already exists in this organization. Please choose a different name."
+ )
raise ValidationError(organization.add_support_message(message))
try:
@@ -655,12 +684,21 @@ class ServiceInstance(ServalaModelMixin, models.Model):
try:
error_body = json.loads(e.body)
reason = error_body.get("message", str(e))
- message = f"Kubernetes API error: {reason}."
+ formatted_reason = cls._format_kubernetes_error(reason)
+ message = _("Error reported by control plane: {reason}").format(
+ reason=formatted_reason
+ )
raise ValidationError(organization.add_support_message(message))
except (ValueError, TypeError):
- message = f"Kubernetes API error: {str(e)}."
+ formatted_error = cls._format_kubernetes_error(str(e))
+ message = _("Error reported by control plane: {error}").format(
+ error=formatted_error
+ )
raise ValidationError(organization.add_support_message(message))
- message = f"Error creating instance: {str(e)}."
+ formatted_error = cls._format_kubernetes_error(str(e))
+ message = _("Error creating instance: {error}").format(
+ error=formatted_error
+ )
raise ValidationError(organization.add_support_message(message))
return instance
@@ -682,18 +720,29 @@ class ServiceInstance(ServalaModelMixin, models.Model):
self.save() # Updates updated_at timestamp
except ApiException as e:
if e.status == 404:
- message = "Service instance not found in Kubernetes. It may have been deleted externally."
+ message = _(
+ "Service instance not found in control plane. It may have been deleted externally."
+ )
raise ValidationError(self.organization.add_support_message(message))
try:
error_body = json.loads(e.body)
reason = error_body.get("message", str(e))
- message = f"Kubernetes API error updating instance: {reason}."
+ formatted_reason = self._format_kubernetes_error(reason)
+ message = _(
+ "Error reported by control plane while updating instance: {reason}"
+ ).format(reason=formatted_reason)
raise ValidationError(self.organization.add_support_message(message))
except (ValueError, TypeError):
- message = f"Kubernetes API error updating instance: {str(e)}."
+ formatted_error = self._format_kubernetes_error(str(e))
+ message = _(
+ "Error reported by control plane while updating instance: {error}"
+ ).format(error=formatted_error)
raise ValidationError(self.organization.add_support_message(message))
except Exception as e:
- message = f"Error updating instance: {str(e)}."
+ formatted_error = self._format_kubernetes_error(str(e))
+ message = _("Error updating instance: {error}").format(
+ error=formatted_error
+ )
raise ValidationError(self.organization.add_support_message(message))
@transaction.atomic
diff --git a/src/servala/frontend/views/service.py b/src/servala/frontend/views/service.py
index be1b80c..f9ce50d 100644
--- a/src/servala/frontend/views/service.py
+++ b/src/servala/frontend/views/service.py
@@ -146,7 +146,9 @@ class ServiceOfferingDetailView(OrganizationViewMixin, HtmxViewMixin, DetailView
if not form: # Should not happen if context_object is valid, but as a safeguard
messages.error(
self.request,
- self.organization.add_support_message("Could not initialize service form."),
+ self.organization.add_support_message(
+ _("Could not initialize service form.")
+ ),
)
return self.render_to_response(context)
@@ -166,7 +168,7 @@ class ServiceOfferingDetailView(OrganizationViewMixin, HtmxViewMixin, DetailView
messages.error(
self.request,
self.organization.add_support_message(
- f"Error creating instance: {str(e)}."
+ _(f"Error creating instance: {str(e)}.")
),
)
@@ -374,7 +376,7 @@ class ServiceInstanceUpdateView(
messages.error(
self.request,
self.organization.add_support_message(
- f"Error updating instance: {str(e)}."
+ _(f"Error updating instance: {str(e)}.")
),
)
return self.form_invalid(form)
@@ -445,7 +447,9 @@ class ServiceInstanceDeleteView(
messages.error(
self.request,
self.organization.add_support_message(
- f"An error occurred while trying to delete instance '{self.object.name}': {str(e)}."
+ _(
+ f"An error occurred while trying to delete instance '{self.object.name}': {str(e)}."
+ )
),
)
response = HttpResponse()