Repository: TencentBlueKing/bk-user

Issue:
[bug] 未配置完成的目录过一段时间之后变成已配置
<img width="1556" alt="image" src="https://user-images.githubusercontent.com/2002216/217460859-77d6c121-55c2-4808-aca1-09b3368a9157.png">

<img width="1568" alt="image" src="https://user-images.githubusercontent.com/2002216/217460903-b3286937-d490-49c6-b0e6-50b1e7dce40e.png">

状态改变的间隔是不确定的, 有时很短, 有时很长


Discussion:
```
>>> from bkuser_core.categories.models import ProfileCategory
>>> c = ProfileCategory.objects.all()
>>> [(x.type, x.configured) for x in c]
[('local', True), ('ldap', True), ('local', True), ('ldap', False)]
>>> c = ProfileCategory.objects.all()
>>> [(x.type, x.configured) for x in c]
[('local', True), ('ldap', True), ('local', True), ('ldap', True)]
>>> 
```
创建

```
+----+----------------------------+----------------------------+------+-------------+--------------+--------+---------+---------+------------------+-------+--------+
| id | create_time                | update_time                | type | description | display_name | domain | default | enabled | last_synced_time | order | status |
+----+----------------------------+----------------------------+------+-------------+--------------+--------+---------+---------+------------------+-------+--------+
|  4 | 2023-02-08 06:23:03.030841 | 2023-02-08 06:23:03.030882 | ldap | NULL        | cccc         | dddd   |       0 |       1 | NULL             |     4 | normal |
+----+----------------------------+----------------------------+------+-------------+--------------+--------+---------+---------+------------------+-------+--------+
```


```
MariaDB [bk_user_api]> select * from user_settings_setting where category_id=4;
+-----+----------------------------+----------------------------+------------------------------------+---------+-------------+---------+
| id  | create_time                | update_time                | value                              | enabled | category_id | meta_id |
+-----+----------------------------+----------------------------+------------------------------------+---------+-------------+---------+
|  92 | 2023-02-08 07:06:54.018848 | 2023-02-08 07:06:54.018894 | 1000                               |       1 |           4 |      82 |
|  93 | 2023-02-08 07:06:54.027118 | 2023-02-08 07:06:54.027149 | 60                                 |       1 |           4 |      83 |
|  94 | 2023-02-08 07:06:54.034736 | 2023-02-08 07:06:54.034766 | 120                                |       1 |           4 |      84 |
|  95 | 2023-02-08 07:06:54.042458 | 2023-02-08 07:06:54.042488 | "memberOf"                         |       1 |           4 |      56 |
|  96 | 2023-02-08 07:06:54.051264 | 2023-02-08 07:06:54.051291 | "description"                      |       1 |           4 |      54 |
|  97 | 2023-02-08 07:06:54.058388 | 2023-02-08 07:06:54.058417 | "cn"                               |       1 |           4 |      53 |
|  98 | 2023-02-08 07:06:54.067515 | 2023-02-08 07:06:54.067542 | "(objectclass=groupOfUniqueNames)" |       1 |           4 |      52 |
|  99 | 2023-02-08 07:06:54.074959 | 2023-02-08 07:06:54.074987 | "groupOfUniqueNames"               |       1 |           4 |      51 |
| 100 | 2023-02-08 07:06:54.082539 | 2023-02-08 07:06:54.082567 | "telephonenumber"                  |       1 |           4 |      48 |
| 101 | 2023-02-08 07:06:54.092661 | 2023-02-08 07:06:54.092705 | "email"                            |       1 |           4 |      47 |
| 102 | 2023-02-08 07:06:54.102767 | 2023-02-08 07:06:54.102810 | "displayName"                      |       1 |           4 |      46 |
| 103 | 2023-02-08 07:06:54.114992 | 2023-02-08 07:06:54.115022 | "cn"                               |       1 |           4 |      45 |
| 104 | 2023-02-08 07:06:54.129717 | 2023-02-08 07:06:54.129745 | "organizationalUnit"               |       1 |           4 |      44 |
| 105 | 2023-02-08 07:06:54.139389 | 2023-02-08 07:06:54.139427 | "(objectclass=inetorgperson)"      |       1 |           4 |      43 |
| 106 | 2023-02-08 07:06:54.153208 | 2023-02-08 07:06:54.153235 | "inetorgperson"                    |       1 |           4 |      42 |
| 107 | 2023-02-08 07:06:54.160993 | 2023-02-08 07:06:54.161021 | ""                                 |       1 |           4 |      41 |
| 108 | 2023-02-08 07:06:54.169596 | 2023-02-08 07:06:54.169624 | {}                                 |       1 |           4 |      58 |
| 109 | 2023-02-08 07:06:54.177568 | 2023-02-08 07:06:54.177595 | ""                                 |       1 |           4 |      40 |
| 110 | 2023-02-08 07:06:54.186012 | 2023-02-08 07:06:54.186049 | ""                                 |       1 |           4 |      39 |
| 111 | 2023-02-08 07:06:54.195840 | 2023-02-08 07:06:54.195867 | ""                                 |       1 |           4 |      38 |
| 112 | 2023-02-08 07:06:54.203371 | 2023-02-08 07:06:54.203398 | 60                                 |       1 |           4 |      37 |
| 113 | 2023-02-08 07:06:54.211040 | 2023-02-08 07:06:54.211067 | 120                                |       1 |           4 |      36 |
| 114 | 2023-02-08 07:06:54.218564 | 2023-02-08 07:06:54.218591 | "\u65e0"                           |       1 |           4 |      35 |
| 115 | 2023-02-08 07:06:54.226021 | 2023-02-08 07:06:54.226048 | ""                                 |       1 |           4 |      34 |
+-----+----------------------------+----------------------------+------------------------------------+---------+-------------+---------+
```

- 创建: 2023-02-08 06:23:03.030882 此时状态对的
- 配置被插入了默认值: 2023-02-08 07:06:54.018848

https://github.com/wklken/bk-user/blob/0ea23edd0eada25ed953648f2075383f3bccc601/src/api/bkuser_core/categories/plugins/plugin.py#L98-L115

worker 重载时就会导致更新, 为啥要更新? 
```
# 默认在创建 meta 后创建 settings，保证新增的配置能够被正确初始化
```

逻辑是, 如果meta新增一个字段, 存量的目录也能初始化到这个字段的默认值

但是, 这样会导致, 存量没有做任何配置的目录会全部被放入默认值 => 导致状态变成 已配置

Resolving patch:
diff --git a/src/api/bkuser_core/categories/plugins/plugin.py b/src/api/bkuser_core/categories/plugins/plugin.py
--- a/src/api/bkuser_core/categories/plugins/plugin.py
+++ b/src/api/bkuser_core/categories/plugins/plugin.py
@@ -70,7 +70,7 @@ def register(self):
         if self.settings_path is not None:
             self.load_settings_from_yaml()
 
-    def init_settings(self, setting_meta_key: str, meta_info: dict):
+    def init_settings(self, setting_meta_key: str, meta_info: dict, category_configured_dict: Dict[int, bool]):
         namespace = meta_info.pop("namespace", "general")
 
         try:
@@ -99,6 +99,14 @@ def init_settings(self, setting_meta_key: str, meta_info: dict):
             # 理论上目录不能够被直接恢复, 所以已经被删除的目录不会被更新
             # 仅做新增，避免覆盖已有配置
             for category in ProfileCategory.objects.filter(type=self.category_type, enabled=True):
+                # if we don't know the category is configured or not, skip it for now
+                if category.id not in category_configured_dict:
+                    continue
+
+                # if the category is not configured, skip it
+                if not category_configured_dict[category.id]:
+                    continue
+
                 try:
                     ins, created = Setting.objects.get_or_create(
                         meta=meta, category_id=category.id, defaults={"value": meta.default}
@@ -114,11 +122,16 @@ def init_settings(self, setting_meta_key: str, meta_info: dict):
                     )
                     continue
 
+    # FIXME: 这类初始化是否不应该放在 registry? (每次程序加载都会执行registry)
     def load_settings_from_yaml(self):
         """从 yaml 中加载 SettingMeta 配置"""
+        category_configured_dict = {}
+        for category in ProfileCategory.objects.filter(type=self.category_type, enabled=True):
+            category_configured_dict[category.id] = category.configured
+
         with self.settings_path.open(mode="r") as f:
             for key, meta_info in yaml.safe_load(f).items():
-                self.init_settings(key, meta_info)
+                self.init_settings(key, meta_info, category_configured_dict)
 
     def get_hook(self, type_: HookType) -> Optional[PluginHook]:
         hook_cls = self.hooks.get(type_)


Test patch:
diff --git a/src/api/bkuser_core/tests/categories/plugins/test_plugin.py b/src/api/bkuser_core/tests/categories/plugins/test_plugin.py
--- a/src/api/bkuser_core/tests/categories/plugins/test_plugin.py
+++ b/src/api/bkuser_core/tests/categories/plugins/test_plugin.py
@@ -86,7 +86,7 @@ def test_load_settings(self, plugin, test_category, settings, expected):
         """test load settings"""
 
         for key, meta_info in settings.items():
-            plugin.init_settings(key, meta_info)
+            plugin.init_settings(key, meta_info, {test_category.id: True})
 
         assert (
             list(
@@ -106,14 +106,14 @@ def test_load_no_update(self, plugin, test_category):
         update_time = meta.update_time
 
         time.sleep(0.1)
-        plugin.init_settings("foo", {"region": "a", "namespace": "b", "default": "wasd"})
+        plugin.init_settings("foo", {"region": "a", "namespace": "b", "default": "wasd"}, {test_category.id: True})
         new_update_time = SettingMeta.objects.get(category_type="test", key="foo", namespace="b").update_time
 
         assert update_time == new_update_time
 
         # region 可以被修改
         time.sleep(0.1)
-        plugin.init_settings("foo", {"region": "www", "namespace": "b"})
+        plugin.init_settings("foo", {"region": "www", "namespace": "b"}, {test_category.id: True})
         meta = SettingMeta.objects.get(category_type="test", key="foo", namespace="b")
         assert update_time != meta.update_time
         assert meta.region == "www"