diff --git a/pyproject.toml b/pyproject.toml
index 3cded0f..27d42c7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -43,7 +43,7 @@ known_first_party = "servala"
[tool.flake8]
max-line-length = 160
exclude = ".venv"
-ignore = "E203,W503"
+ignore = "E203"
[tool.djlint]
extend_exclude = "src/servala/static/mazer"
diff --git a/src/servala/core/models/service.py b/src/servala/core/models/service.py
index 2f83d8d..550a489 100644
--- a/src/servala/core/models/service.py
+++ b/src/servala/core/models/service.py
@@ -609,34 +609,61 @@ class ServiceInstance(ServalaModelMixin, models.Model):
@cached_property
def connection_credentials(self):
"""
- Get connection credentials directly from the resource's writeConnectionSecretToRef
- after checking that secret conditions are available.
+ Get connection credentials via spec.resourceRef.
+ The resource referenced there has the information which secret
+ we want in spec.writeConnectionSecretToRef.name and spec.writeConnectionSecretToRef.namespace.
"""
if not self.kubernetes_object:
return {}
-
- # Check if secrets are available based on conditions
- secrets_available = any(
- [
- condition.get("type") == "Status" and condition.get("status") == "True"
- for condition in self.status_conditions
- ]
- )
- if not secrets_available:
- return {}
-
- if not (secret_ref := self.spec.get("writeConnectionSecretToRef")):
- return {}
- if not (secret_name := secret_ref.get("name")):
+ if not (
+ resource_ref := self.kubernetes_object.get("spec", {}).get("resourceRef")
+ ):
return {}
try:
+ group = resource_ref.get("apiVersion", "").split("/")[0]
+ version = resource_ref.get("apiVersion", "").split("/")[1]
+ kind = resource_ref.get("kind")
+ name = resource_ref.get("name")
+ namespace = resource_ref.get("namespace", self.organization.namespace)
+
+ if not all([group, version, kind, name]):
+ return {}
+
+ plural = kind.lower()
+ if not plural.endswith("s"):
+ plural = f"{plural}s"
+
+ api_instance = client.CustomObjectsApi(
+ self.context.control_plane.get_kubernetes_client()
+ )
+
+ referenced_obj = api_instance.get_namespaced_custom_object(
+ group=group,
+ version=version,
+ namespace=namespace,
+ plural=plural,
+ name=name,
+ )
+
+ secret_ref = referenced_obj.get("spec", {}).get(
+ "writeConnectionSecretToRef"
+ )
+ if not secret_ref:
+ return {}
+
+ secret_name = secret_ref.get("name")
+ secret_namespace = secret_ref.get("namespace", namespace)
+
+ if not secret_name:
+ return {}
+
# Get the secret data
v1 = kubernetes.client.CoreV1Api(
self.context.control_plane.get_kubernetes_client()
)
secret = v1.read_namespaced_secret(
- name=secret_name, namespace=secret_ref.get("namespace")
+ name=secret_name, namespace=secret_namespace
)
# Secret data is base64 encoded
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 b9cf4dc..f83fb84 100644
--- a/src/servala/frontend/templates/frontend/organizations/service_instance_detail.html
+++ b/src/servala/frontend/templates/frontend/organizations/service_instance_detail.html
@@ -103,58 +103,29 @@
-
-
-
-
- {% for fieldset in spec_fieldsets %}
- -
- {{ fieldset.title }}
-
+
+
+
+
+ {% translate "Property" %} |
+ {% translate "Value" %} |
+
+
+
+ {% for key, value in instance.spec.items %}
+
+ {{ key }} |
+
+ {% if value|default:""|stringformat:"s"|slice:":1" == "{" or value|default:""|stringformat:"s"|slice:":1" == "[" %}
+ {{ value|pprint }}
+ {% else %}
+ {{ value }}
+ {% endif %}
+ |
+
{% endfor %}
-
-
-
- {% for fieldset in spec_fieldsets %}
-
-
-
- {% for field in fieldset.fields %}
- - {{ field.label }}
- -
- {% if field.value|default:""|stringformat:"s"|slice:":1" == "{" or field.value|default:""|stringformat:"s"|slice:":1" == "[" %}
-
{{ field.value|pprint }}
- {% else %}
- {{ field.value }}
- {% endif %}
-
- {% endfor %}
-
-
- {% for sub_key, sub_fieldset in fieldset.fieldsets.items %}
-
{{ sub_fieldset.title }}
-
- {% for field in sub_fieldset.fields %}
- - {{ field.label }}
- -
- {% if field.value|default:""|stringformat:"s"|slice:":1" == "{" or field.value|default:""|stringformat:"s"|slice:":1" == "[" %}
-
{{ field.value|pprint }}
- {% else %}
- {{ field.value }}
- {% endif %}
-
- {% endfor %}
-
- {% endfor %}
-
- {% endfor %}
-
-
+
+
diff --git a/src/servala/frontend/views/service.py b/src/servala/frontend/views/service.py
index b6ecc42..6ee6b1e 100644
--- a/src/servala/frontend/views/service.py
+++ b/src/servala/frontend/views/service.py
@@ -4,7 +4,6 @@ from django.shortcuts import redirect
from django.utils.functional import cached_property
from django.views.generic import DetailView, ListView
-from servala.core.crd import deslugify
from servala.core.models import (
ControlPlaneCRD,
Service,
@@ -169,116 +168,6 @@ class ServiceInstanceDetailView(OrganizationViewMixin, DetailView):
"context__service_definition__service",
)
- def get_nested_spec(self):
- """
- Organize spec data into fieldsets similar to how the form does it.
- """
- spec = self.object.spec or {}
-
- # Process spec fields
- others = []
- nested_fieldsets = {}
-
- # First pass: organize fields into nested structures
- for key, value in spec.items():
- if isinstance(value, dict):
- # This is a nested structure
- if key not in nested_fieldsets:
- nested_fieldsets[key] = {
- "title": deslugify(key),
- "fields": [],
- "fieldsets": {},
- }
-
- # Process fields in the nested structure
- for sub_key, sub_value in value.items():
- if isinstance(sub_value, dict):
- # Even deeper nesting
- if sub_key not in nested_fieldsets[key]["fieldsets"]:
- nested_fieldsets[key]["fieldsets"][sub_key] = {
- "title": deslugify(sub_key),
- "fields": [],
- }
-
- # Add fields from the deeper level
- for leaf_key, leaf_value in sub_value.items():
- nested_fieldsets[key]["fieldsets"][sub_key][
- "fields"
- ].append(
- {
- "key": leaf_key,
- "label": deslugify(leaf_key),
- "value": leaf_value,
- }
- )
- else:
- # Add field to parent level
- nested_fieldsets[key]["fields"].append(
- {
- "key": sub_key,
- "label": deslugify(sub_key),
- "value": sub_value,
- }
- )
- else:
- # This is a top-level field
- others.append(
- {
- "key": key,
- "label": deslugify(key),
- "value": value,
- }
- )
-
- # Second pass: Promote fields based on count
- for group_key, group in list(nested_fieldsets.items()):
- # Promote single sub-fieldsets to parent
- for sub_key, sub_fieldset in list(group["fieldsets"].items()):
- if len(sub_fieldset["fields"]) == 1:
- field = sub_fieldset["fields"][0]
- field["label"] = f"{sub_fieldset['title']}: {field['label']}"
- group["fields"].append(field)
- del group["fieldsets"][sub_key]
-
- # Move single-field groups to others
- total_fields = len(group["fields"])
- for sub_fieldset in group["fieldsets"].values():
- total_fields += len(sub_fieldset["fields"])
-
- if (
- total_fields == 1
- and len(group["fields"]) == 1
- and not group["fieldsets"]
- ):
- field = group["fields"][0]
- field["label"] = f"{group['title']}: {field['label']}"
- others.append(field)
- del nested_fieldsets[group_key]
- elif total_fields == 0:
- del nested_fieldsets[group_key]
-
- fieldsets = []
- if others:
- fieldsets.append(
- {
- "title": "General",
- "fields": others,
- "fieldsets": {},
- }
- )
- # Create fieldsets from the organized data
- for group in nested_fieldsets.values():
- fieldsets.append(group)
-
- return fieldsets
-
- def get_context_data(self, **kwargs):
- """Return service instance for the current organization."""
- context = super().get_context_data(**kwargs)
- if self.object.spec:
- context["spec_fieldsets"] = self.get_nested_spec()
- return context
-
class ServiceInstanceListView(OrganizationViewMixin, ListView):
template_name = "frontend/organizations/service_instances.html"