Rename model, reset migrations

This commit is contained in:
Tobias Kunze 2025-03-31 11:24:57 +02:00
parent 94713a3100
commit 67f4b3ba12
15 changed files with 462 additions and 767 deletions

View file

@ -6,6 +6,7 @@ from servala.core.models import (
BillingEntity, BillingEntity,
CloudProvider, CloudProvider,
ControlPlane, ControlPlane,
ControlPlaneCRD,
Organization, Organization,
OrganizationMembership, OrganizationMembership,
OrganizationOrigin, OrganizationOrigin,
@ -15,7 +16,6 @@ from servala.core.models import (
ServiceDefinition, ServiceDefinition,
ServiceInstance, ServiceInstance,
ServiceOffering, ServiceOffering,
ServiceOfferingControlPlane,
User, User,
) )
@ -207,14 +207,14 @@ class ServiceDefinitionAdmin(admin.ModelAdmin):
return ["api_definition"] return ["api_definition"]
class ServiceOfferingControlPlaneInline(admin.TabularInline): class ControlPlaneCRDInline(admin.TabularInline):
model = ServiceOfferingControlPlane model = ControlPlaneCRD
extra = 1 extra = 1
autocomplete_fields = ("control_plane", "service_definition") autocomplete_fields = ("control_plane", "service_definition")
@admin.register(ServiceOfferingControlPlane) @admin.register(ControlPlaneCRD)
class ServiceOfferingControlPlaneAdmin(admin.ModelAdmin): class ControlPlaneCRDAdmin(admin.ModelAdmin):
list_display = ("service_offering", "control_plane", "service_definition") list_display = ("service_offering", "control_plane", "service_definition")
list_filter = ("service_offering", "control_plane", "service_definition") list_filter = ("service_offering", "control_plane", "service_definition")
search_fields = ("service_offering__service__name", "control_plane__name") search_fields = ("service_offering__service__name", "control_plane__name")
@ -263,6 +263,6 @@ class ServiceOfferingAdmin(admin.ModelAdmin):
search_fields = ("description",) search_fields = ("description",)
autocomplete_fields = ("service", "provider") autocomplete_fields = ("service", "provider")
inlines = ( inlines = (
ServiceOfferingControlPlaneInline, ControlPlaneCRDInline,
PlanInline, PlanInline,
) )

View file

@ -1,9 +1,13 @@
# Generated by Django 5.2b1 on 2025-03-16 08:44 # Generated by Django 5.2b1 on 2025-03-31 09:20
import django.core.validators
import django.db.models.deletion import django.db.models.deletion
import encrypted_fields.fields
import rules.contrib.models
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
import servala.core.models.service
import servala.core.models.user import servala.core.models.user
@ -11,9 +15,128 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [] dependencies = [
("auth", "0012_alter_user_first_name_max_length"),
]
operations = [ operations = [
migrations.CreateModel(
name="BillingEntity",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="Created"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
("name", models.CharField(max_length=100, verbose_name="Name")),
(
"description",
models.TextField(blank=True, verbose_name="Description"),
),
(
"erp_reference",
models.CharField(
blank=True, max_length=100, verbose_name="ERP reference"
),
),
],
options={
"verbose_name": "Billing entity",
"verbose_name_plural": "Billing entities",
},
bases=(rules.contrib.models.RulesModelMixin, models.Model),
),
migrations.CreateModel(
name="CloudProvider",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="Created"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
("name", models.CharField(max_length=100, verbose_name="Name")),
(
"description",
models.TextField(blank=True, verbose_name="Description"),
),
(
"logo",
models.ImageField(
blank=True,
null=True,
upload_to="public/service_providers",
verbose_name="Logo",
),
),
(
"external_links",
models.JSONField(
blank=True, null=True, verbose_name="External links"
),
),
],
options={
"verbose_name": "Cloud provider",
"verbose_name_plural": "Cloud providers",
},
bases=(rules.contrib.models.RulesModelMixin, models.Model),
),
migrations.CreateModel(
name="OrganizationOrigin",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="Created"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
("name", models.CharField(max_length=100, verbose_name="Name")),
(
"description",
models.TextField(blank=True, verbose_name="Description"),
),
],
options={
"verbose_name": "Organization origin",
"verbose_name_plural": "Organization origins",
},
bases=(rules.contrib.models.RulesModelMixin, models.Model),
),
migrations.CreateModel( migrations.CreateModel(
name="User", name="User",
fields=[ fields=[
@ -33,6 +156,14 @@ class Migration(migrations.Migration):
blank=True, null=True, verbose_name="last login" blank=True, null=True, verbose_name="last login"
), ),
), ),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="Created"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
( (
"email", "email",
models.EmailField( models.EmailField(
@ -73,105 +204,38 @@ class Migration(migrations.Migration):
verbose_name="Is superuser", verbose_name="Is superuser",
), ),
), ),
(
"groups",
models.ManyToManyField(
blank=True,
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
related_name="user_set",
related_query_name="user",
to="auth.group",
verbose_name="groups",
),
),
(
"user_permissions",
models.ManyToManyField(
blank=True,
help_text="Specific permissions for this user.",
related_name="user_set",
related_query_name="user",
to="auth.permission",
verbose_name="user permissions",
),
),
], ],
options={ options={
"verbose_name": "User", "verbose_name": "User",
"verbose_name_plural": "Users", "verbose_name_plural": "Users",
}, },
bases=(rules.contrib.models.RulesModelMixin, models.Model),
managers=[ managers=[
("objects", servala.core.models.user.UserManager()), ("objects", servala.core.models.user.UserManager()),
], ],
), ),
migrations.CreateModel(
name="BillingEntity",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=100, verbose_name="Name")),
(
"description",
models.TextField(blank=True, verbose_name="Description"),
),
(
"erp_reference",
models.CharField(
blank=True, max_length=100, verbose_name="ERP reference"
),
),
],
options={
"verbose_name": "Billing entity",
"verbose_name_plural": "Billing entities",
},
),
migrations.CreateModel(
name="CloudProvider",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=100, verbose_name="Name")),
(
"description",
models.TextField(blank=True, verbose_name="Description"),
),
(
"logo",
models.ImageField(
blank=True,
null=True,
upload_to="public/service_providers",
verbose_name="Logo",
),
),
(
"external_links",
models.JSONField(
blank=True, null=True, verbose_name="External links"
),
),
],
options={
"verbose_name": "Cloud provider",
"verbose_name_plural": "Cloud providers",
},
),
migrations.CreateModel(
name="OrganizationOrigin",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=100, verbose_name="Name")),
(
"description",
models.TextField(blank=True, verbose_name="Description"),
),
],
options={
"verbose_name": "Organization origin",
"verbose_name_plural": "Organization origins",
},
),
migrations.CreateModel( migrations.CreateModel(
name="ControlPlane", name="ControlPlane",
fields=[ fields=[
@ -184,16 +248,29 @@ class Migration(migrations.Migration):
verbose_name="ID", verbose_name="ID",
), ),
), ),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="Created"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
("name", models.CharField(max_length=100, verbose_name="Name")), ("name", models.CharField(max_length=100, verbose_name="Name")),
( (
"description", "description",
models.TextField(blank=True, verbose_name="Description"), models.TextField(blank=True, verbose_name="Description"),
), ),
( (
"k8s_api_endpoint", "api_credentials",
models.URLField(verbose_name="Kubernetes API endpoint"), encrypted_fields.fields.EncryptedJSONField(
help_text="Required fields: certificate-authority-data, server (URL), token",
validators=[
servala.core.models.service.validate_api_credentials
],
verbose_name="API credentials",
),
), ),
("api_credentials", models.JSONField(verbose_name="API credentials")),
( (
"cloud_provider", "cloud_provider",
models.ForeignKey( models.ForeignKey(
@ -208,6 +285,7 @@ class Migration(migrations.Migration):
"verbose_name": "Control plane", "verbose_name": "Control plane",
"verbose_name_plural": "Control planes", "verbose_name_plural": "Control planes",
}, },
bases=(rules.contrib.models.RulesModelMixin, models.Model),
), ),
migrations.CreateModel( migrations.CreateModel(
name="Organization", name="Organization",
@ -221,10 +299,35 @@ class Migration(migrations.Migration):
verbose_name="ID", verbose_name="ID",
), ),
), ),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="Created"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
("name", models.CharField(max_length=100, verbose_name="Name")), ("name", models.CharField(max_length=100, verbose_name="Name")),
(
"namespace",
models.CharField(
help_text="This namespace will be used for all Kubernetes resources. Cannot be changed after creation.",
max_length=63,
unique=True,
validators=[
django.core.validators.RegexValidator(
code="invalid_kubernetes_name",
message='Name must consist of lowercase alphanumeric characters or "-", must start and end with an alphanumeric character.',
regex="^[a-z0-9]([-a-z0-9]*[a-z0-9])?$",
)
],
verbose_name="Kubernetes Namespace",
),
),
( (
"billing_entity", "billing_entity",
models.ForeignKey( models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.PROTECT, on_delete=django.db.models.deletion.PROTECT,
related_name="organizations", related_name="organizations",
to="core.billingentity", to="core.billingentity",
@ -236,6 +339,7 @@ class Migration(migrations.Migration):
"verbose_name": "Organization", "verbose_name": "Organization",
"verbose_name_plural": "Organizations", "verbose_name_plural": "Organizations",
}, },
bases=(rules.contrib.models.RulesModelMixin, models.Model),
), ),
migrations.CreateModel( migrations.CreateModel(
name="OrganizationMembership", name="OrganizationMembership",
@ -249,6 +353,14 @@ class Migration(migrations.Migration):
verbose_name="ID", verbose_name="ID",
), ),
), ),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="Created"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
( (
"date_joined", "date_joined",
models.DateTimeField(auto_now_add=True, verbose_name="Date joined"), models.DateTimeField(auto_now_add=True, verbose_name="Date joined"),
@ -289,6 +401,7 @@ class Migration(migrations.Migration):
"verbose_name": "Organization membership", "verbose_name": "Organization membership",
"verbose_name_plural": "Organization memberships", "verbose_name_plural": "Organization memberships",
}, },
bases=(rules.contrib.models.RulesModelMixin, models.Model),
), ),
migrations.AddField( migrations.AddField(
model_name="organization", model_name="organization",
@ -322,6 +435,14 @@ class Migration(migrations.Migration):
verbose_name="ID", verbose_name="ID",
), ),
), ),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="Created"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
("name", models.CharField(max_length=100, verbose_name="Name")), ("name", models.CharField(max_length=100, verbose_name="Name")),
( (
"description", "description",
@ -352,6 +473,7 @@ class Migration(migrations.Migration):
"verbose_name": "Service category", "verbose_name": "Service category",
"verbose_name_plural": "Service categories", "verbose_name_plural": "Service categories",
}, },
bases=(rules.contrib.models.RulesModelMixin, models.Model),
), ),
migrations.CreateModel( migrations.CreateModel(
name="Service", name="Service",
@ -365,7 +487,21 @@ class Migration(migrations.Migration):
verbose_name="ID", verbose_name="ID",
), ),
), ),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="Created"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
("name", models.CharField(max_length=100, verbose_name="Name")), ("name", models.CharField(max_length=100, verbose_name="Name")),
(
"slug",
models.SlugField(
max_length=100, unique=True, verbose_name="URL slug"
),
),
( (
"description", "description",
models.TextField(blank=True, verbose_name="Description"), models.TextField(blank=True, verbose_name="Description"),
@ -399,6 +535,57 @@ class Migration(migrations.Migration):
"verbose_name": "Service", "verbose_name": "Service",
"verbose_name_plural": "Services", "verbose_name_plural": "Services",
}, },
bases=(rules.contrib.models.RulesModelMixin, models.Model),
),
migrations.CreateModel(
name="ServiceDefinition",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="Created"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
("name", models.CharField(max_length=100, verbose_name="Name")),
(
"description",
models.TextField(blank=True, verbose_name="Description"),
),
(
"api_definition",
models.JSONField(
blank=True,
help_text="Contains group, version, and kind information",
null=True,
verbose_name="API Definition",
),
),
(
"service",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="service_definitions",
to="core.service",
verbose_name="Service",
),
),
],
options={
"verbose_name": "Service definition",
"verbose_name_plural": "Service definitions",
},
bases=(rules.contrib.models.RulesModelMixin, models.Model),
), ),
migrations.CreateModel( migrations.CreateModel(
name="ServiceOffering", name="ServiceOffering",
@ -413,16 +600,16 @@ class Migration(migrations.Migration):
), ),
), ),
( (
"description", "created_at",
models.TextField(blank=True, verbose_name="Description"), models.DateTimeField(auto_now_add=True, verbose_name="Created"),
), ),
( (
"control_plane", "updated_at",
models.ManyToManyField( models.DateTimeField(auto_now=True, verbose_name="Last updated"),
related_name="offerings", ),
to="core.controlplane", (
verbose_name="Control planes", "description",
), models.TextField(blank=True, verbose_name="Description"),
), ),
( (
"provider", "provider",
@ -447,6 +634,7 @@ class Migration(migrations.Migration):
"verbose_name": "Service offering", "verbose_name": "Service offering",
"verbose_name_plural": "Service offerings", "verbose_name_plural": "Service offerings",
}, },
bases=(rules.contrib.models.RulesModelMixin, models.Model),
), ),
migrations.CreateModel( migrations.CreateModel(
name="Plan", name="Plan",
@ -460,6 +648,14 @@ class Migration(migrations.Migration):
verbose_name="ID", verbose_name="ID",
), ),
), ),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="Created"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
("name", models.CharField(max_length=100, verbose_name="Name")), ("name", models.CharField(max_length=100, verbose_name="Name")),
( (
"description", "description",
@ -493,5 +689,142 @@ class Migration(migrations.Migration):
"verbose_name": "Plan", "verbose_name": "Plan",
"verbose_name_plural": "Plans", "verbose_name_plural": "Plans",
}, },
bases=(rules.contrib.models.RulesModelMixin, models.Model),
),
migrations.CreateModel(
name="ControlPlaneCRD",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="Created"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
(
"control_plane",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="offering_connections",
to="core.controlplane",
verbose_name="Control plane",
),
),
(
"service_definition",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="offering_control_planes",
to="core.servicedefinition",
verbose_name="Service definition",
),
),
(
"service_offering",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="control_plane_connections",
to="core.serviceoffering",
verbose_name="Service offering",
),
),
],
options={
"verbose_name": "Service offering control plane connection",
"verbose_name_plural": "Service offering control planes connections",
"unique_together": {("service_offering", "control_plane")},
},
bases=(rules.contrib.models.RulesModelMixin, models.Model),
),
migrations.CreateModel(
name="ServiceInstance",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="Created"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
(
"name",
models.CharField(
max_length=63,
validators=[
django.core.validators.RegexValidator(
code="invalid_kubernetes_name",
message='Name must consist of lowercase alphanumeric characters or "-", must start and end with an alphanumeric character.',
regex="^[a-z0-9]([-a-z0-9]*[a-z0-9])?$",
)
],
verbose_name="Name",
),
),
("is_deleted", models.BooleanField(default=False)),
("deleted_at", models.DateTimeField(blank=True, null=True)),
(
"context",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="service_instances",
to="core.controlplanecrd",
),
),
(
"created_by",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="+",
to=settings.AUTH_USER_MODEL,
),
),
(
"deleted_by",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="+",
to=settings.AUTH_USER_MODEL,
),
),
(
"organization",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="service_instances",
to="core.organization",
verbose_name="Organization",
),
),
],
options={
"verbose_name": "Service instance",
"verbose_name_plural": "Service instances",
"unique_together": {("name", "organization", "context")},
},
bases=(rules.contrib.models.RulesModelMixin, models.Model),
), ),
] ]

View file

@ -1,89 +0,0 @@
# Generated by Django 5.2b1 on 2025-03-17 06:19
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0001_initial"),
]
operations = [
migrations.AddField(
model_name="billingentity",
name="created_at",
field=models.DateTimeField(
auto_now_add=True,
default=django.utils.timezone.now,
verbose_name="Created",
),
preserve_default=False,
),
migrations.AddField(
model_name="billingentity",
name="updated_at",
field=models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
migrations.AddField(
model_name="organization",
name="created_at",
field=models.DateTimeField(
auto_now_add=True,
default=django.utils.timezone.now,
verbose_name="Created",
),
preserve_default=False,
),
migrations.AddField(
model_name="organization",
name="updated_at",
field=models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
migrations.AddField(
model_name="organizationmembership",
name="created_at",
field=models.DateTimeField(
auto_now_add=True,
default=django.utils.timezone.now,
verbose_name="Created",
),
preserve_default=False,
),
migrations.AddField(
model_name="organizationmembership",
name="updated_at",
field=models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
migrations.AddField(
model_name="organizationorigin",
name="created_at",
field=models.DateTimeField(
auto_now_add=True,
default=django.utils.timezone.now,
verbose_name="Created",
),
preserve_default=False,
),
migrations.AddField(
model_name="organizationorigin",
name="updated_at",
field=models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
migrations.AddField(
model_name="user",
name="created_at",
field=models.DateTimeField(
auto_now_add=True,
default=django.utils.timezone.now,
verbose_name="Created",
),
preserve_default=False,
),
migrations.AddField(
model_name="user",
name="updated_at",
field=models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
]

View file

@ -1,25 +0,0 @@
# Generated by Django 5.2b1 on 2025-03-20 08:12
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0002_billingentity_created_at_billingentity_updated_at_and_more"),
]
operations = [
migrations.AlterField(
model_name="organization",
name="billing_entity",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="organizations",
to="core.billingentity",
verbose_name="Billing entity",
),
),
]

View file

@ -1,46 +0,0 @@
# Generated by Django 5.2b1 on 2025-03-24 06:33
import encrypted_fields.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("auth", "0012_alter_user_first_name_max_length"),
("core", "0003_billing_entity_nullable"),
]
operations = [
migrations.AddField(
model_name="user",
name="groups",
field=models.ManyToManyField(
blank=True,
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
related_name="user_set",
related_query_name="user",
to="auth.group",
verbose_name="groups",
),
),
migrations.AddField(
model_name="user",
name="user_permissions",
field=models.ManyToManyField(
blank=True,
help_text="Specific permissions for this user.",
related_name="user_set",
related_query_name="user",
to="auth.permission",
verbose_name="user permissions",
),
),
migrations.AlterField(
model_name="controlplane",
name="api_credentials",
field=encrypted_fields.fields.EncryptedJSONField(
verbose_name="API credentials"
),
),
]

View file

@ -1,29 +0,0 @@
# Generated by Django 5.2b1 on 2025-03-24 10:27
import encrypted_fields.fields
from django.db import migrations
import servala.core.models.service
class Migration(migrations.Migration):
dependencies = [
("core", "0004_encrypt_api_credentials"),
]
operations = [
migrations.RemoveField(
model_name="controlplane",
name="k8s_api_endpoint",
),
migrations.AlterField(
model_name="controlplane",
name="api_credentials",
field=encrypted_fields.fields.EncryptedJSONField(
help_text="Required fields: certificate-authority-data, server (URL), token",
validators=[servala.core.models.service.validate_api_credentials],
verbose_name="API credentials",
),
),
]

View file

@ -1,21 +0,0 @@
# Generated by Django 5.2b1 on 2025-03-24 14:29
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0005_remove_controlplane_k8s_api_endpoint"),
]
operations = [
migrations.AddField(
model_name="service",
name="slug",
field=models.SlugField(
default="slug", max_length=100, unique=True, verbose_name="URL slug"
),
preserve_default=False,
),
]

View file

@ -1,115 +0,0 @@
# Generated by Django 5.2b1 on 2025-03-25 11:02
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0006_service_slug"),
]
operations = [
migrations.RemoveField(
model_name="serviceoffering",
name="control_plane",
),
migrations.CreateModel(
name="ServiceDefinition",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=100, verbose_name="Name")),
(
"description",
models.TextField(blank=True, verbose_name="Description"),
),
(
"api_definition",
models.JSONField(
blank=True,
help_text="Contains group, version, and kind information",
null=True,
verbose_name="API Definition",
),
),
(
"service",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="service_definitions",
to="core.service",
verbose_name="Service",
),
),
],
options={
"verbose_name": "Service definition",
"verbose_name_plural": "Service definitions",
},
),
migrations.CreateModel(
name="ServiceOfferingControlPlane",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"control_plane",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="offering_connections",
to="core.controlplane",
verbose_name="Control plane",
),
),
(
"service_definition",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="offering_control_planes",
to="core.servicedefinition",
verbose_name="Service definition",
),
),
(
"service_offering",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="control_plane_connections",
to="core.serviceoffering",
verbose_name="Service offering",
),
),
],
options={
"verbose_name": "Service offering control plane connection",
"verbose_name_plural": "Service offering control planes connections",
"unique_together": {("service_offering", "control_plane")},
},
),
migrations.AddField(
model_name="serviceoffering",
name="control_planes",
field=models.ManyToManyField(
related_name="offerings",
through="core.ServiceOfferingControlPlane",
to="core.controlplane",
verbose_name="Control planes",
),
),
]

View file

@ -1,134 +0,0 @@
# Generated by Django 5.2b1 on 2025-03-26 14:54
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0007_service_definition"),
]
operations = [
migrations.AddField(
model_name="cloudprovider",
name="created_at",
field=models.DateTimeField(
auto_now_add=True,
default=django.utils.timezone.now,
verbose_name="Created",
),
preserve_default=False,
),
migrations.AddField(
model_name="cloudprovider",
name="updated_at",
field=models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
migrations.AddField(
model_name="controlplane",
name="created_at",
field=models.DateTimeField(
auto_now_add=True,
default=django.utils.timezone.now,
verbose_name="Created",
),
preserve_default=False,
),
migrations.AddField(
model_name="controlplane",
name="updated_at",
field=models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
migrations.AddField(
model_name="plan",
name="created_at",
field=models.DateTimeField(
auto_now_add=True,
default=django.utils.timezone.now,
verbose_name="Created",
),
preserve_default=False,
),
migrations.AddField(
model_name="plan",
name="updated_at",
field=models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
migrations.AddField(
model_name="service",
name="created_at",
field=models.DateTimeField(
auto_now_add=True,
default=django.utils.timezone.now,
verbose_name="Created",
),
preserve_default=False,
),
migrations.AddField(
model_name="service",
name="updated_at",
field=models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
migrations.AddField(
model_name="servicecategory",
name="created_at",
field=models.DateTimeField(
auto_now_add=True,
default=django.utils.timezone.now,
verbose_name="Created",
),
preserve_default=False,
),
migrations.AddField(
model_name="servicecategory",
name="updated_at",
field=models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
migrations.AddField(
model_name="servicedefinition",
name="created_at",
field=models.DateTimeField(
auto_now_add=True,
default=django.utils.timezone.now,
verbose_name="Created",
),
preserve_default=False,
),
migrations.AddField(
model_name="servicedefinition",
name="updated_at",
field=models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
migrations.AddField(
model_name="serviceoffering",
name="created_at",
field=models.DateTimeField(
auto_now_add=True,
default=django.utils.timezone.now,
verbose_name="Created",
),
preserve_default=False,
),
migrations.AddField(
model_name="serviceoffering",
name="updated_at",
field=models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
migrations.AddField(
model_name="serviceofferingcontrolplane",
name="created_at",
field=models.DateTimeField(
auto_now_add=True,
default=django.utils.timezone.now,
verbose_name="Created",
),
preserve_default=False,
),
migrations.AddField(
model_name="serviceofferingcontrolplane",
name="updated_at",
field=models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
]

View file

@ -1,34 +0,0 @@
# Generated by Django 5.2b1 on 2025-03-28 09:39
import django.core.validators
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0008_created_and_updated"),
]
operations = [
migrations.AddField(
model_name="organization",
name="namespace",
field=models.CharField(
default="namespace",
help_text="This namespace will be used for all Kubernetes resources. Cannot be changed after creation.",
max_length=63,
unique=True,
validators=[
django.core.validators.RegexValidator(
code="invalid_namespace",
message='Namespace must consist of lowercase alphanumeric characters or "-", must start and end with an alphanumeric character.',
regex="^[a-z0-9]([-a-z0-9]*[a-z0-9])?$",
)
],
verbose_name="Kubernetes Namespace",
),
preserve_default=False,
),
]

View file

@ -1,117 +0,0 @@
# Generated by Django 5.2b1 on 2025-03-28 10:29
import django.core.validators
import django.db.models.deletion
import rules.contrib.models
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0009_organization_namespace"),
]
operations = [
migrations.AlterField(
model_name="organization",
name="namespace",
field=models.CharField(
help_text="This namespace will be used for all Kubernetes resources. Cannot be changed after creation.",
max_length=63,
unique=True,
validators=[
django.core.validators.RegexValidator(
code="invalid_kubernetes_name",
message='Name must consist of lowercase alphanumeric characters or "-", must start and end with an alphanumeric character.',
regex="^[a-z0-9]([-a-z0-9]*[a-z0-9])?$",
)
],
verbose_name="Kubernetes Namespace",
),
),
migrations.AlterField(
model_name="servicecategory",
name="name",
field=models.CharField(
max_length=100,
validators=[
django.core.validators.RegexValidator(
code="invalid_kubernetes_name",
message='Name must consist of lowercase alphanumeric characters or "-", must start and end with an alphanumeric character.',
regex="^[a-z0-9]([-a-z0-9]*[a-z0-9])?$",
)
],
verbose_name="Name",
),
),
migrations.CreateModel(
name="ServiceInstance",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="Created"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="Last updated"),
),
("name", models.CharField(max_length=100, verbose_name="Name")),
("is_deleted", models.BooleanField(default=False)),
("deleted_at", models.DateTimeField(blank=True, null=True)),
(
"context",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="service_instances",
to="core.serviceofferingcontrolplane",
),
),
(
"created_by",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="+",
to=settings.AUTH_USER_MODEL,
),
),
(
"deleted_by",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="+",
to=settings.AUTH_USER_MODEL,
),
),
(
"organization",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="service_instances",
to="core.organization",
verbose_name="Organization",
),
),
],
options={
"verbose_name": "Service instance",
"verbose_name_plural": "Service instances",
"unique_together": {("name", "organization", "context")},
},
bases=(rules.contrib.models.RulesModelMixin, models.Model),
),
]

View file

@ -1,34 +0,0 @@
# Generated by Django 5.2b1 on 2025-03-28 11:51
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0010_service_instance"),
]
operations = [
migrations.AlterField(
model_name="servicecategory",
name="name",
field=models.CharField(max_length=100, verbose_name="Name"),
),
migrations.AlterField(
model_name="serviceinstance",
name="name",
field=models.CharField(
max_length=63,
validators=[
django.core.validators.RegexValidator(
code="invalid_kubernetes_name",
message='Name must consist of lowercase alphanumeric characters or "-", must start and end with an alphanumeric character.',
regex="^[a-z0-9]([-a-z0-9]*[a-z0-9])?$",
)
],
verbose_name="Name",
),
),
]

View file

@ -8,13 +8,13 @@ from .organization import (
from .service import ( from .service import (
CloudProvider, CloudProvider,
ControlPlane, ControlPlane,
ControlPlaneCRD,
Plan, Plan,
Service, Service,
ServiceCategory, ServiceCategory,
ServiceDefinition, ServiceDefinition,
ServiceInstance, ServiceInstance,
ServiceOffering, ServiceOffering,
ServiceOfferingControlPlane,
) )
from .user import User from .user import User
@ -22,6 +22,7 @@ __all__ = [
"BillingEntity", "BillingEntity",
"CloudProvider", "CloudProvider",
"ControlPlane", "ControlPlane",
"ControlPlaneCRD",
"Organization", "Organization",
"OrganizationMembership", "OrganizationMembership",
"OrganizationOrigin", "OrganizationOrigin",
@ -32,6 +33,5 @@ __all__ = [
"ServiceInstance", "ServiceInstance",
"ServiceDefinition", "ServiceDefinition",
"ServiceOffering", "ServiceOffering",
"ServiceOfferingControlPlane",
"User", "User",
] ]

View file

@ -121,6 +121,12 @@ class ControlPlane(ServalaModelMixin, models.Model):
def __str__(self): def __str__(self):
return self.name return self.name
@property
def service_definitions(self):
return ServiceDefinition.objects.filter(
offering_control_planes__control_plane=self
).distinct()
@property @property
def kubernetes_config(self): def kubernetes_config(self):
conf = kubernetes.client.Configuration() conf = kubernetes.client.Configuration()
@ -288,10 +294,10 @@ class ServiceDefinition(ServalaModelMixin, models.Model):
return self.name return self.name
class ServiceOfferingControlPlane(ServalaModelMixin, models.Model): class ControlPlaneCRD(ServalaModelMixin, models.Model):
""" """
Each combination of ServiceOffering and ControlPlane can have a different Each combination of ServiceOffering and ControlPlane can have a different
ServiceDefinition, which is here modeled as the "through" model. ServiceDefinition, which is here modeled as basically a "through" model.
""" """
service_offering = models.ForeignKey( service_offering = models.ForeignKey(
@ -391,12 +397,6 @@ class ServiceOffering(ServalaModelMixin, models.Model):
related_name="offerings", related_name="offerings",
verbose_name=_("Provider"), verbose_name=_("Provider"),
) )
control_planes = models.ManyToManyField(
to="ControlPlane",
through="ServiceOfferingControlPlane",
related_name="offerings",
verbose_name=_("Control planes"),
)
description = models.TextField(blank=True, verbose_name=_("Description")) description = models.TextField(blank=True, verbose_name=_("Description"))
class Meta: class Meta:
@ -408,6 +408,12 @@ class ServiceOffering(ServalaModelMixin, models.Model):
service_name=self.service.name, provider_name=self.provider.name service_name=self.service.name, provider_name=self.provider.name
) )
@property
def control_planes(self):
return ControlPlane.objects.filter(
offering_connections__service_offering=self
).distinct()
class ServiceInstance(ServalaModelMixin, models.Model): class ServiceInstance(ServalaModelMixin, models.Model):
""" """
@ -433,7 +439,7 @@ class ServiceInstance(ServalaModelMixin, models.Model):
related_name="+", related_name="+",
) )
context = models.ForeignKey( context = models.ForeignKey(
to="core.ServiceOfferingControlPlane", to="core.ControlPlaneCRD",
related_name="service_instances", related_name="service_instances",
on_delete=models.PROTECT, on_delete=models.PROTECT,
) )

View file

@ -4,10 +4,10 @@ from django.utils.functional import cached_property
from django.views.generic import DetailView, ListView from django.views.generic import DetailView, ListView
from servala.core.models import ( from servala.core.models import (
ControlPlaneCRD,
Service, Service,
ServiceInstance, ServiceInstance,
ServiceOffering, ServiceOffering,
ServiceOfferingControlPlane,
) )
from servala.frontend.forms.service import ControlPlaneSelectForm, ServiceFilterForm from servala.frontend.forms.service import ControlPlaneSelectForm, ServiceFilterForm
from servala.frontend.views.mixins import HtmxViewMixin, OrganizationViewMixin from servala.frontend.views.mixins import HtmxViewMixin, OrganizationViewMixin
@ -89,12 +89,12 @@ class ServiceOfferingDetailView(OrganizationViewMixin, HtmxViewMixin, DetailView
@cached_property @cached_property
def context_object(self): def context_object(self):
if self.request.method == "POST": if self.request.method == "POST":
return ServiceOfferingControlPlane.objects.filter( return ControlPlaneCRD.objects.filter(
pk=self.request.POST.get("context"), pk=self.request.POST.get("context"),
# Make sure we dont use a malicious ID # Make sure we dont use a malicious ID
control_plane__in=self.planes, control_plane__in=self.planes,
).first() ).first()
return ServiceOfferingControlPlane.objects.filter( return ControlPlaneCRD.objects.filter(
control_plane=self.selected_plane, service_offering=self.object control_plane=self.selected_plane, service_offering=self.object
).first() ).first()