Implement plan logic in create/update

This commit is contained in:
Tobias Kunze 2025-12-02 16:09:21 +01:00
parent 2bbd643cf9
commit 29661aa7cd

View file

@ -686,6 +686,60 @@ class ServiceInstance(ServalaModelMixin, models.Model):
spec_data = prune_empty_data(spec_data)
return spec_data
@staticmethod
def _apply_compute_plan_to_spec(spec_data, compute_plan_assignment):
"""
Apply compute plan resource allocations and SLA to spec.
"""
if not compute_plan_assignment:
return spec_data
compute_plan = compute_plan_assignment.compute_plan
if "parameters" not in spec_data:
spec_data["parameters"] = {}
if "size" not in spec_data["parameters"]:
spec_data["parameters"]["size"] = {}
if "requests" not in spec_data["parameters"]["size"]:
spec_data["parameters"]["size"]["requests"] = {}
if "service" not in spec_data["parameters"]:
spec_data["parameters"]["service"] = {}
spec_data["parameters"]["size"]["memory"] = compute_plan.memory_limits
spec_data["parameters"]["size"]["cpu"] = compute_plan.cpu_limits
spec_data["parameters"]["size"]["requests"][
"memory"
] = compute_plan.memory_requests
spec_data["parameters"]["size"]["requests"]["cpu"] = compute_plan.cpu_requests
spec_data["parameters"]["service"]["serviceLevel"] = compute_plan_assignment.sla
return spec_data
@staticmethod
def _build_billing_annotations(compute_plan_assignment, control_plane):
"""
Build Kubernetes annotations for billing integration.
"""
annotations = {}
if compute_plan_assignment:
annotations["servala.com/erp_product_id_resource"] = str(
compute_plan_assignment.odoo_product_id
)
annotations["servala.com/erp_unit_id_resource"] = str(
compute_plan_assignment.odoo_unit_id
)
if control_plane.storage_plan_odoo_product_id:
annotations["servala.com/erp_product_id_storage"] = str(
control_plane.storage_plan_odoo_product_id
)
if control_plane.storage_plan_odoo_unit_id:
annotations["servala.com/erp_unit_id_storage"] = str(
control_plane.storage_plan_odoo_unit_id
)
return annotations
@classmethod
def _format_kubernetes_error(cls, error_message):
if not error_message:
@ -740,7 +794,15 @@ class ServiceInstance(ServalaModelMixin, models.Model):
@classmethod
@transaction.atomic
def create_instance(cls, name, organization, context, created_by, spec_data):
def create_instance(
cls,
name,
organization,
context,
created_by,
spec_data,
compute_plan_assignment=None,
):
# Ensure the namespace exists
context.control_plane.get_or_create_namespace(organization)
try:
@ -749,6 +811,7 @@ class ServiceInstance(ServalaModelMixin, models.Model):
organization=organization,
created_by=created_by,
context=context,
compute_plan_assignment=compute_plan_assignment,
)
except IntegrityError:
message = _(
@ -759,6 +822,11 @@ class ServiceInstance(ServalaModelMixin, models.Model):
try:
spec_data = cls._prepare_spec_data(spec_data)
if compute_plan_assignment:
spec_data = cls._apply_compute_plan_to_spec(
spec_data, compute_plan_assignment
)
if "writeConnectionSecretToRef" not in spec_data:
spec_data["writeConnectionSecretToRef"] = {}
@ -776,6 +844,13 @@ class ServiceInstance(ServalaModelMixin, models.Model):
},
"spec": spec_data,
}
annotations = cls._build_billing_annotations(
compute_plan_assignment, context.control_plane
)
if annotations:
create_data["metadata"]["annotations"] = annotations
if label := context.control_plane.required_label:
create_data["metadata"]["labels"] = {settings.DEFAULT_LABEL_KEY: label}
api_instance = context.control_plane.custom_objects_api
@ -813,12 +888,23 @@ class ServiceInstance(ServalaModelMixin, models.Model):
raise ValidationError(organization.add_support_message(message))
return instance
def update_spec(self, spec_data, updated_by):
def update_spec(self, spec_data, updated_by, compute_plan_assignment=None):
try:
spec_data = self._prepare_spec_data(spec_data)
plan_to_use = compute_plan_assignment or self.compute_plan_assignment
if plan_to_use:
spec_data = self._apply_compute_plan_to_spec(spec_data, plan_to_use)
api_instance = self.context.control_plane.custom_objects_api
patch_body = {"spec": spec_data}
annotations = self._build_billing_annotations(
plan_to_use, self.context.control_plane
)
if annotations:
patch_body["metadata"] = {"annotations": annotations}
api_instance.patch_namespaced_custom_object(
group=self.context.group,
version=self.context.version,
@ -828,7 +914,14 @@ class ServiceInstance(ServalaModelMixin, models.Model):
body=patch_body,
)
self._clear_kubernetes_caches()
self.save() # Updates updated_at timestamp
if (
compute_plan_assignment
and compute_plan_assignment != self.compute_plan_assignment
):
self.compute_plan_assignment = compute_plan_assignment
# Saving to update updated_at timestamp even if nothing was visibly changed
self.save()
except ApiException as e:
if e.status == 404:
message = _(