Skip to content

Commit 34ee942

Browse files
feat: paas-service plan 支持多租户 (TencentBlueKing#211)
1 parent 753b09f commit 34ee942

13 files changed

Lines changed: 133 additions & 21 deletions

File tree

sdks/paas-service/CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
# 版本历史
2+
## 2.0.2
3+
- Support multi tenant
24

35
## 2.0.1
46
- Support querying service instance with parameter to_be_deleted

sdks/paas-service/paas_service/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@
1616
We undertake not to change the open source license (MIT license) applicable
1717
to the current version of the project delivered to anyone in the future.
1818
"""
19-
__version__ = '2.0.1'
19+
__version__ = '2.0.2'
2020

2121
default_app_config = 'paas_service.apps.PaaSServiceConfig'

sdks/paas-service/paas_service/constants.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,8 @@ class Category(int, Enum):
2828

2929
# Login 服务的重定向链接字段名
3030
REDIRECT_FIELD_NAME = "c_url"
31+
32+
# The default tenant id exists only if the project does not enable multi-tenant mode,
33+
# it serves as a reserved value. When multi-tenant mode is enabled, no tenant id can
34+
# be "default".
35+
DEFAULT_TENANT_ID = "default"
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# -*- coding: utf-8 -*-
2+
# TencentBlueKing is pleased to support the open source community by making
3+
# 蓝鲸智云 - PaaS 平台 (BlueKing - PaaS System) available.
4+
# Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
5+
# Licensed under the MIT License (the "License"); you may not use this file except
6+
# in compliance with the License. You may obtain a copy of the License at
7+
#
8+
# http://opensource.org/licenses/MIT
9+
#
10+
# Unless required by applicable law or agreed to in writing, software distributed under
11+
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
12+
# either express or implied. See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
# We undertake not to change the open source license (MIT license) applicable
16+
# to the current version of the project delivered to anyone in the future.
17+
from django.db import models
18+
19+
from paas_service.constants import DEFAULT_TENANT_ID
20+
21+
22+
def tenant_id_field_factory(db_index: bool = True, unique: bool = False) -> models.CharField:
23+
"""Create a field that is configured to store tenant_id.
24+
25+
:param db_index: Whether to create an index for the field, defaults to True, turn
26+
it off when the model already has a compound index on the tenant_id field.
27+
:param unique: Whether to create a unique index for the field, defaults to False.
28+
29+
NOTE: https://github.com/TencentBlueKing/blueking-paas/pull/1877#discussion_r1912873811 中有相关的设计讨论
30+
"""
31+
# If unique is True, db_index should be disabled to avoid creating a redundant index.
32+
if unique:
33+
db_index = False
34+
return models.CharField(
35+
verbose_name="租户 ID",
36+
max_length=32,
37+
default=DEFAULT_TENANT_ID,
38+
help_text="本条数据的所属租户",
39+
db_index=db_index,
40+
unique=unique,
41+
)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Generated by Django 4.2.16 on 2025-02-17 13:19
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('paas_service', '0008_auto_20220426_0429'),
10+
]
11+
12+
operations = [
13+
migrations.AlterUniqueTogether(
14+
name='plan',
15+
unique_together=set(),
16+
),
17+
migrations.AddField(
18+
model_name='plan',
19+
name='tenant_id',
20+
field=models.CharField(db_index=True, default='default', help_text='本条数据的所属租户', max_length=32, verbose_name='租户 ID'),
21+
),
22+
migrations.AddField(
23+
model_name='serviceinstance',
24+
name='tenant_id',
25+
field=models.CharField(db_index=True, default='default', help_text='本条数据的所属租户', max_length=32, verbose_name='租户 ID'),
26+
),
27+
migrations.AddField(
28+
model_name='serviceinstanceconfig',
29+
name='tenant_id',
30+
field=models.CharField(db_index=True, default='default', help_text='本条数据的所属租户', max_length=32, verbose_name='租户 ID'),
31+
),
32+
migrations.AlterUniqueTogether(
33+
name='plan',
34+
unique_together={('tenant_id', 'service', 'name')},
35+
),
36+
]

sdks/paas-service/paas_service/models.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@
2828
from django.utils.translation import gettext_lazy as _
2929
from jsonfield import JSONField
3030
from translated_fields import TranslatedField
31-
3231
from blue_krill.models.fields import EncryptField
3332

33+
from paas_service.fields import tenant_id_field_factory
34+
35+
3436
# Base Models start
3537

3638

@@ -59,7 +61,10 @@ class Meta(object):
5961

6062

6163
class SpecDefinition(UuidAuditedModel):
62-
"""Specification definition"""
64+
"""Specification definition
65+
66+
[multi-tenancy] This model is not tenant-aware.
67+
"""
6368

6469
index = models.IntegerField(verbose_name=_('顺序'), default=0)
6570
name = models.CharField(verbose_name=_('名称'), unique=True, max_length=64, null=True)
@@ -81,7 +86,10 @@ def save(self, *args, **kwargs):
8186

8287

8388
class Specification(UuidAuditedModel):
84-
"""Specification value"""
89+
"""Specification value
90+
91+
[multi-tenancy] This model is not tenant-aware.
92+
"""
8593

8694
definition = models.ForeignKey(SpecDefinition, verbose_name=_('定义'), on_delete=models.CASCADE)
8795
value = models.CharField(verbose_name=_('值'), max_length=64)
@@ -101,7 +109,10 @@ def save(self, *args, **kwargs):
101109

102110

103111
class Service(UuidAuditedModel):
104-
"""Service model for PaaS"""
112+
"""Service model for PaaS
113+
114+
[multi-tenancy] This model is not tenant-aware.
115+
"""
105116

106117
name = models.CharField(verbose_name='服务名称', unique=True, max_length=64)
107118
category = models.IntegerField(verbose_name='服务分类')
@@ -134,11 +145,13 @@ class ServiceInstance(UuidAuditedModel):
134145

135146
service = models.ForeignKey('Service', verbose_name='服务', null=True, on_delete=models.SET_NULL)
136147
plan = models.ForeignKey(
137-
'Plan', verbose_name='方案', null=True, help_text="当前仅当迁移的增强服务实例, 会没有 plan", on_delete=models.SET_NULL
148+
'Plan', verbose_name='方案', null=True, help_text="当前仅当迁移的增强服务实例, 会没有 plan",
149+
on_delete=models.SET_NULL
138150
)
139151
config = JSONField(default=dict, blank=True)
140152
credentials = EncryptField(default="")
141153
to_be_deleted = models.BooleanField(default=False)
154+
tenant_id = tenant_id_field_factory()
142155

143156
class Meta:
144157
verbose_name_plural = verbose_name = '服务实例'
@@ -174,6 +187,7 @@ class ServiceInstanceConfig(UuidAuditedModel):
174187

175188
instance = models.OneToOneField(ServiceInstance, verbose_name='服务实例', on_delete=models.CASCADE)
176189
paas_app_info = JSONField(default=dict, blank=True, verbose_name='平台应用信息')
190+
tenant_id = tenant_id_field_factory()
177191

178192
def was_initialized(self) -> bool:
179193
"""Return if current config object was initialized"""
@@ -228,10 +242,11 @@ class Plan(UuidAuditedModel):
228242
service = models.ForeignKey('Service', related_name='plans', verbose_name='服务', on_delete=models.CASCADE)
229243

230244
specifications = models.ManyToManyField(Specification, verbose_name='规格', blank=True)
245+
tenant_id = tenant_id_field_factory()
231246

232247
class Meta:
233248
verbose_name_plural = verbose_name = '方案'
234-
unique_together = ("service", "name")
249+
unique_together = ("tenant_id", "service", "name")
235250

236251
def __str__(self):
237252
return f"{self.name}-{self.service.name}"
@@ -254,7 +269,10 @@ def full_specifications(self) -> Dict[str, Any]:
254269

255270

256271
class ResourceId(models.Model):
257-
"""utility model for avoiding name conflict"""
272+
"""utility model for avoiding name conflict
273+
274+
[multi-tenancy] This model is not tenant-aware.
275+
"""
258276

259277
namespace = models.CharField(max_length=32)
260278
uid = models.CharField(max_length=64, null=False, unique=True, db_index=True)

sdks/paas-service/paas_service/serializers.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ class PaaSAppInfoSLZ(serializers.Serializer):
9696

9797
class InstanceConfigSLZ(serializers.Serializer):
9898
paas_app_info = PaaSAppInfoSLZ(required=True)
99+
tenant_id = serializers.CharField(max_length=64, read_only=True)
99100

100101

101102
############################

sdks/paas-service/paas_service/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def gen_unique_id(
6060

6161
def get_paas_app_info(instance: ServiceInstance) -> Optional[Dict]:
6262
"""Get instance's related PaaS app infomations Dict"""
63-
config, _ = ServiceInstanceConfig.objects.get_or_create(instance=instance)
63+
config, _ = ServiceInstanceConfig.objects.get_or_create(instance=instance, tenant_id=instance.tenant_id)
6464
if not config.was_initialized():
6565
return None
6666

sdks/paas-service/paas_service/views.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636

3737
logger = logging.getLogger(__name__)
3838

39-
4039
m_verified_client_required = method_decorator(verified_client_required)
4140

4241

@@ -166,6 +165,7 @@ def provision(self, request, service_id, instance_id):
166165
plan=plan,
167166
config=instance_data.config,
168167
credentials=json.dumps(instance_data.credentials),
168+
tenant_id=plan.tenant_id,
169169
)
170170
service_instance.prerender(request)
171171
return Response(serializers.ServiceInstanceSLZ(service_instance).data, status=201)
@@ -288,7 +288,7 @@ class SvcInstanceConfigViewSet(viewsets.ViewSet):
288288
def retrieve(self, request, instance_id):
289289
"""Retrieve an instance's config"""
290290
instance = get_object_or_404(ServiceInstance, pk=instance_id)
291-
config, _ = ServiceInstanceConfig.objects.get_or_create(instance=instance)
291+
config, _ = ServiceInstanceConfig.objects.get_or_create(instance=instance, tenant_id=instance.tenant_id)
292292
if not config.was_initialized():
293293
return Response(data={'detail': 'config was not initialized yet'}, status=400)
294294

@@ -304,7 +304,8 @@ def update(self, request, instance_id):
304304

305305
# Update config
306306
config, _ = ServiceInstanceConfig.objects.update_or_create(
307-
instance=instance, defaults={'paas_app_info': slz.validated_data['paas_app_info']}
307+
instance=instance, tenant_id=instance.tenant_id,
308+
defaults={'paas_app_info': slz.validated_data['paas_app_info']}
308309
)
309310
resp_slz = serializers.InstanceConfigSLZ(config)
310311
return Response(resp_slz.data)

sdks/paas-service/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[tool]
22
[tool.poetry]
33
name = "paas_service"
4-
version = "2.0.1"
4+
version = "2.0.2"
55
description = "A Django application for developing BK-PaaS add-on services."
66
readme = "README.md"
77
authors = ["blueking <blueking@tencent.com>"]

0 commit comments

Comments
 (0)