mirror of
https://gitee.com/Tencent-BlueKing/bk-cmdb.git
synced 2025-12-06 08:59:15 +08:00
@@ -5,11 +5,11 @@ spec_version: 1
|
||||
release:
|
||||
# 发布版本号;
|
||||
# 资源配置更新,需更新此版本号才会发布资源版本,此版本号和 sdk 版本号一致,错误设置会影响调用方使用
|
||||
version: 3.14.6
|
||||
version: 3.14.7-alpha2
|
||||
# 版本标题
|
||||
title: "3.14.6"
|
||||
title: "3.14.7-alpha2"
|
||||
# 版本描述
|
||||
comment: "3.14.6"
|
||||
comment: "3.14.7-alpha2"
|
||||
|
||||
# 定义网关基本信息,用于命令 `sync_apigw_config`
|
||||
apigateway:
|
||||
@@ -75,6 +75,23 @@ grant_permissions:
|
||||
- list_kube_container
|
||||
- list_cached_kube_pod_label_key
|
||||
- list_cached_kube_pod_label_value
|
||||
- search_biz_inst_topo
|
||||
- get_biz_internal_module
|
||||
- list_biz_hosts
|
||||
- list_hosts_without_biz
|
||||
- find_host_biz_relations
|
||||
- list_biz_hosts_topo
|
||||
- find_module_host_relation
|
||||
- resource_watch
|
||||
- search_business
|
||||
- search_cloud_area
|
||||
- search_object_attribute
|
||||
- find_topo_node_paths
|
||||
- search_dynamic_group
|
||||
- execute_dynamic_group
|
||||
- get_biz_brief_cache_topo
|
||||
- list_business_set
|
||||
- list_business_in_business_set
|
||||
- bk_app_code: {{ environ.BK_NODEMAN_APP_CODE }}
|
||||
grant_dimension: "resource"
|
||||
resource_names:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
### Description
|
||||
|
||||
Add hosts to the business idle hosts. This interface ensures that hosts are either added successfully together or fail
|
||||
together (v3.10.25+, Permission: Host pool host allocation to business permission)
|
||||
together (v3.10.25+, Permission: Edit business host permission)
|
||||
|
||||
### Parameters
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ Clone Host Properties (Permission: Business Host Editing Permission)
|
||||
| bk_org_id | int | Yes | Source host ID |
|
||||
| bk_dst_id | int | Yes | Target host ID |
|
||||
| bk_biz_id | int | Yes | Business ID |
|
||||
| bk_cloud_id | int | No | Control area ID |
|
||||
| bk_cloud_id | int | Yes | Control area ID |
|
||||
|
||||
Note: Cloning using the internal IP of the host and cloning using the identity ID of the host, these two methods can
|
||||
only be used separately and cannot be mixed.
|
||||
|
||||
@@ -58,7 +58,7 @@ business attribute fields. The combination supports only AND operations, can be
|
||||
"bk_biz_set_attr":{
|
||||
"bk_biz_set_name":"biz_set",
|
||||
"bk_biz_set_desc":"xxx",
|
||||
"biz_set_maintainer":"xxx"
|
||||
"bk_biz_maintainer":"xxx"
|
||||
},
|
||||
"bk_scope":{
|
||||
"match_all":false,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Update Module (Permission: Business Topology Editing Permission)
|
||||
|
||||
### Parameters
|
||||
### URL Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|---------------------|--------|----------|-------------------|
|
||||
@@ -10,9 +10,8 @@ Update Module (Permission: Business Topology Editing Permission)
|
||||
| bk_biz_id | int | Yes | Business ID |
|
||||
| bk_set_id | int | Yes | Cluster ID |
|
||||
| bk_module_id | int | Yes | Module ID |
|
||||
| data | dict | Yes | Module data |
|
||||
|
||||
#### data
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|-----------------|--------|----------|-------------------|
|
||||
@@ -21,7 +20,7 @@ Update Module (Permission: Business Topology Editing Permission)
|
||||
| operator | string | No | Main maintainer |
|
||||
| bk_bak_operator | string | No | Backup maintainer |
|
||||
|
||||
**Note: The data parameter here only explains the system-built editable parameters, and the rest of the parameters to be
|
||||
**Note: The parameter here only explains the system-built editable parameters, and the rest of the parameters to be
|
||||
filled depend on the user's own defined attribute fields. Modules created through service templates can only be modified
|
||||
through service templates.**
|
||||
|
||||
@@ -29,15 +28,10 @@ through service templates.**
|
||||
|
||||
```json
|
||||
{
|
||||
"bk_biz_id": 1,
|
||||
"bk_set_id": 1,
|
||||
"bk_module_id": 1,
|
||||
"data": {
|
||||
"bk_module_name": "test",
|
||||
"bk_module_type": "1",
|
||||
"operator": "admin",
|
||||
"bk_bak_operator": "admin"
|
||||
}
|
||||
"bk_module_name": "test",
|
||||
"bk_module_type": "1",
|
||||
"operator": "admin",
|
||||
"bk_bak_operator": "admin"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
新增主机到业务空闲机
|
||||
|
||||
- 此接口保证主机要么同时添加成功,要么同时失败(v3.10.25+,权限:主机池主机分配到业务权限)
|
||||
- 此接口保证主机要么同时添加成功,要么同时失败(v3.10.25+,权限:业务主机编辑权限)
|
||||
|
||||
### 输入参数
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
| bk_org_id | int | 是 | 源主机ID |
|
||||
| bk_dst_id | int | 是 | 目标主机ID |
|
||||
| bk_biz_id | int | 是 | 业务ID |
|
||||
| bk_cloud_id | int | 否 | 管控区域ID |
|
||||
| bk_cloud_id | int | 是 | 管控区域ID |
|
||||
|
||||
注: 使用主机内网IP进行克隆与使用主机身份ID进行克隆,这两种方式只能使用期中的一种,不能混用。
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
"bk_biz_set_attr":{
|
||||
"bk_biz_set_name":"biz_set",
|
||||
"bk_biz_set_desc":"xxx",
|
||||
"biz_set_maintainer":"xxx"
|
||||
"bk_biz_maintainer":"xxx"
|
||||
},
|
||||
"bk_scope":{
|
||||
"match_all":false,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
更新模块(权限:业务拓扑编辑权限)
|
||||
|
||||
### 输入参数
|
||||
### URL输入参数
|
||||
|
||||
| 参数名称 | 参数类型 | 必选 | 描述 |
|
||||
|---------------------|--------|----|-------|
|
||||
@@ -10,9 +10,8 @@
|
||||
| bk_biz_id | int | 是 | 业务id |
|
||||
| bk_set_id | int | 是 | 集群id |
|
||||
| bk_module_id | int | 是 | 模块id |
|
||||
| data | dict | 是 | 模块数据 |
|
||||
|
||||
#### data
|
||||
#### 输入参数
|
||||
|
||||
| 参数名称 | 参数类型 | 必选 | 描述 |
|
||||
|-----------------|--------|----|-------|
|
||||
@@ -21,22 +20,16 @@
|
||||
| operator | string | 否 | 主要维护人 |
|
||||
| bk_bak_operator | string | 否 | 备份维护人 |
|
||||
|
||||
**注意:此处data参数仅对系统内置可编辑的参数做了说明,其余需要填写的参数取决于用户自己定义的属性字段;通过服务模板创建的模块,只能通过服务模板修改
|
||||
**
|
||||
**注意:此处输入参数仅对系统内置可编辑的参数做了说明,其余需要填写的参数取决于用户自己定义的属性字段;通过服务模板创建的模块,只能通过服务模板修改**
|
||||
|
||||
### 调用示例
|
||||
|
||||
```json
|
||||
{
|
||||
"bk_biz_id": 1,
|
||||
"bk_set_id": 1,
|
||||
"bk_module_id": 1,
|
||||
"data": {
|
||||
"bk_module_name": "test",
|
||||
"bk_module_type": "1",
|
||||
"operator": "admin",
|
||||
"bk_bak_operator": "admin"
|
||||
}
|
||||
"bk_module_name": "test",
|
||||
"bk_module_type": "1",
|
||||
"operator": "admin",
|
||||
"bk_bak_operator": "admin"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
| 参数名称 | 参数类型 | 必选 | 描述 |
|
||||
|---------------------|--------|----|---------------|
|
||||
| bk_biz_id | int | 是 | 业务ID |
|
||||
| process_template_id | int | 是 | 进程模板ID |
|
||||
| process_property | object | 是 | 需要更新的进程模板字段信息 |
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
specVersion: 3
|
||||
appVersion: "3.14.6"
|
||||
appVersion: "3.14.7-alpha2"
|
||||
app:
|
||||
bkAppCode: "bk_cmdb_saas"
|
||||
bkAppName: "蓝鲸配置平台"
|
||||
|
||||
@@ -1,3 +1,40 @@
|
||||
## [Version: v3.14.7-alpha2] - 2025-09-08
|
||||
**功能优化**
|
||||
- 导出文件名增加时间戳
|
||||
- 主机与实例导出成功后退出不再提示确认关闭
|
||||
- 动态分组IP默认分割
|
||||
- 为job添加默认apigw接口权限
|
||||
- apigw接口描述国际化
|
||||
|
||||
**缺陷修复**
|
||||
- 修复枚举类型字段默认值选择异常的问题
|
||||
|
||||
## [Version: v3.14.7-alpha1] - 2025-08-06
|
||||
**新增功能**
|
||||
- 接入审计中心
|
||||
- 添加前端版本更新检查并提示刷新功能
|
||||
- 字段类型支持插件
|
||||
|
||||
**功能优化**
|
||||
- 前端不使用proxy接口代理从用户管理查询用户列表的接口
|
||||
- 删除gse目录并处理一些codeql扫描问题
|
||||
- 更新license
|
||||
- 用户态和应用态apigw接口文档调整
|
||||
|
||||
**缺陷修复**
|
||||
- 采集主机数据中IP随机排序问题
|
||||
- 修复切换业务集时出现参数错误的提示问题
|
||||
- 修复业务集新建后操作无权限无法展示资源名称的问题
|
||||
- 修复动态分组日期类型条件出现重复项的问题
|
||||
- 修复动态分组条件类型保存错误
|
||||
- 添加资源主机转移操作预鉴权
|
||||
- 动态分组主机条件in或not in时查询错误
|
||||
- 新增主机到模块选择主机后点击下一步后页面显示空白
|
||||
- 项目列表多出一个项目图标字段
|
||||
- 集群模板-英文状态下更新模版属性后“同步功能”不支持点击跳转了
|
||||
- 服务实例-批量编辑标签时没有及时触发校验规则
|
||||
- 修复主机/实例高级搜索 操作符为contains或者contains(cs)时输入\报错的问题
|
||||
|
||||
## [Version: v3.14.6] - 2025-05-28
|
||||
**功能优化**
|
||||
- mongodb、redis、elasticsearch支持tls连接
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM hub.bktencent.com/blueking/centos7-cmdb:base
|
||||
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
|
||||
ENV container docker
|
||||
COPY cmdb_adminserver /data/cmdb/cmdb_adminserver
|
||||
RUN mkdir /data/cmdb/cmdb_adminserver/logs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM hub.bktencent.com/blueking/centos7-cmdb:base
|
||||
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
|
||||
ENV container docker
|
||||
COPY cmdb_apiserver /data/cmdb/cmdb_apiserver
|
||||
RUN mkdir /data/cmdb/cmdb_apiserver/logs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM hub.bktencent.com/blueking/centos7-cmdb:base
|
||||
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
|
||||
ENV container docker
|
||||
COPY cmdb_authserver /data/cmdb/cmdb_authserver
|
||||
RUN mkdir /data/cmdb/cmdb_authserver/logs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM hub.bktencent.com/blueking/centos7-cmdb:base
|
||||
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
|
||||
ENV container docker
|
||||
COPY cmdb_cacheservice /data/cmdb/cmdb_cacheservice
|
||||
RUN mkdir /data/cmdb/cmdb_cacheservice/logs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM hub.bktencent.com/blueking/centos7-cmdb:base
|
||||
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
|
||||
ENV container docker
|
||||
COPY cmdb_cloudserver /data/cmdb/cmdb_cloudserver
|
||||
RUN mkdir /data/cmdb/cmdb_cloudserver/logs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM hub.bktencent.com/blueking/centos7-cmdb:base
|
||||
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
|
||||
ENV container docker
|
||||
COPY cmdb_coreservice /data/cmdb/cmdb_coreservice
|
||||
RUN mkdir /data/cmdb/cmdb_coreservice/logs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM hub.bktencent.com/blueking/centos7-cmdb:base
|
||||
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
|
||||
ENV container docker
|
||||
COPY cmdb_datacollection /data/cmdb/cmdb_datacollection
|
||||
RUN mkdir /data/cmdb/cmdb_datacollection/logs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM hub.bktencent.com/blueking/centos7-cmdb:base
|
||||
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
|
||||
ENV container docker
|
||||
COPY cmdb_eventserver /data/cmdb/cmdb_eventserver
|
||||
RUN mkdir /data/cmdb/cmdb_eventserver/logs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM hub.bktencent.com/blueking/centos7-cmdb:base
|
||||
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
|
||||
ENV container docker
|
||||
COPY cmdb_hostserver /data/cmdb/cmdb_hostserver
|
||||
RUN mkdir /data/cmdb/cmdb_hostserver/logs
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
FROM hub.bktencent.com/blueking/centos7-cmdb:base
|
||||
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
|
||||
RUN yum -y install jq
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM hub.bktencent.com/blueking/centos7-cmdb:base
|
||||
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
|
||||
ENV container docker
|
||||
COPY cmdb_monstache /data/cmdb/monstache
|
||||
RUN chmod +x /data/cmdb/monstache/monstache
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM hub.bktencent.com/blueking/centos7-cmdb:base
|
||||
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
|
||||
ENV container docker
|
||||
COPY cmdb_operationserver /data/cmdb/cmdb_operationserver
|
||||
RUN mkdir /data/cmdb/cmdb_operationserver/logs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM hub.bktencent.com/blueking/centos7-cmdb:base
|
||||
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
|
||||
ENV container docker
|
||||
COPY cmdb_procserver /data/cmdb/cmdb_procserver
|
||||
RUN mkdir /data/cmdb/cmdb_procserver/logs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM hub.bktencent.com/blueking/centos7-cmdb:base
|
||||
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
|
||||
ENV container docker
|
||||
COPY cmdb_synchronizeserver /data/cmdb/cmdb_synchronizeserver
|
||||
RUN mkdir /data/cmdb/cmdb_synchronizeserver/logs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM hub.bktencent.com/blueking/centos7-cmdb:base
|
||||
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
|
||||
ENV container docker
|
||||
COPY cmdb_taskserver /data/cmdb/cmdb_taskserver
|
||||
RUN mkdir /data/cmdb/cmdb_taskserver/logs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM hub.bktencent.com/blueking/centos7-cmdb:base
|
||||
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
|
||||
ENV container docker
|
||||
COPY cmdb_toposerver /data/cmdb/cmdb_toposerver
|
||||
RUN mkdir /data/cmdb/cmdb_toposerver/logs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM hub.bktencent.com/blueking/centos7-cmdb:base
|
||||
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
|
||||
ENV container docker
|
||||
COPY cmdb_transferservice /data/cmdb/cmdb_transferservice
|
||||
RUN mkdir /data/cmdb/cmdb_transferservice/logs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM hub.bktencent.com/blueking/centos7-cmdb:base
|
||||
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
|
||||
ENV container docker
|
||||
COPY cmdb_webserver /data/cmdb/cmdb_webserver
|
||||
RUN mkdir /data/cmdb/cmdb_webserver/logs
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
apiVersion: v2
|
||||
appVersion: 3.14.6
|
||||
appVersion: 3.14.7-alpha2
|
||||
description: BlueKing Configuration Management DataBase (bk-cmdb) is an enterprise level configuration management serivce database.
|
||||
name: bk-cmdb
|
||||
type: application
|
||||
version: 3.15.6
|
||||
version: 3.15.7-alpha2
|
||||
dependencies:
|
||||
- name: common
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
|
||||
@@ -306,6 +306,14 @@ data:
|
||||
# 用于解密根据RFC1423加密的证书密钥的PEM块
|
||||
password: {{ .Values.common.apiGW.tls.password }}
|
||||
|
||||
# 审计中心数据上报配置
|
||||
auditCenter:
|
||||
enabled: {{ .Values.common.auditCenter.enabled }}
|
||||
appCode: {{ .Values.common.auditCenter.appCode }}
|
||||
appSecret: {{ .Values.common.auditCenter.appSecret }}
|
||||
endpoint: {{ .Values.common.auditCenter.endpoint }}
|
||||
token: {{ .Values.common.auditCenter.token }}
|
||||
|
||||
extra.yaml: |-
|
||||
|
||||
|
||||
|
||||
@@ -1420,6 +1420,12 @@ common:
|
||||
caFile:
|
||||
# 用于解密根据RFC1423加密的证书密钥的PEM块
|
||||
password:
|
||||
auditCenter:
|
||||
enabled: false
|
||||
appCode:
|
||||
appSecret:
|
||||
endpoint:
|
||||
token:
|
||||
|
||||
## @section zookeeper parameters
|
||||
##
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
apiVersion: v2
|
||||
appVersion: 3.14.6
|
||||
appVersion: 3.14.7-alpha2
|
||||
description: BlueKing Configuration Management DataBase (bk-cmdb) is an enterprise level configuration management serivce database.
|
||||
name: bk-cmdb-web
|
||||
type: application
|
||||
version: 3.15.6
|
||||
version: 3.15.7-alpha2
|
||||
dependencies:
|
||||
- name: common
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
|
||||
25
go.mod
25
go.mod
@@ -23,7 +23,7 @@ require (
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2
|
||||
github.com/gomodule/redigo v2.0.0+incompatible
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/google/uuid v1.4.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/sessions v1.2.1
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/juju/ratelimit v1.0.1
|
||||
@@ -64,7 +64,11 @@ require (
|
||||
stathat.com/c/consistent v1.0.0
|
||||
)
|
||||
|
||||
require github.com/mozillazg/go-pinyin v0.20.0
|
||||
require (
|
||||
github.com/mozillazg/go-pinyin v0.20.0
|
||||
go.opentelemetry.io/proto/otlp v1.3.1
|
||||
google.golang.org/protobuf v1.34.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect
|
||||
@@ -87,11 +91,11 @@ require (
|
||||
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/gorilla/context v1.1.1 // indirect
|
||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.2 // indirect
|
||||
@@ -141,16 +145,15 @@ require (
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.17.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.19.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.33.0 // indirect
|
||||
golang.org/x/net v0.35.0 // indirect
|
||||
golang.org/x/crypto v0.35.0 // indirect
|
||||
golang.org/x/image v0.18.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
|
||||
google.golang.org/grpc v1.61.1 // indirect
|
||||
google.golang.org/protobuf v1.36.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 // indirect
|
||||
google.golang.org/grpc v1.64.0 // indirect
|
||||
gopkg.in/ini.v1 v1.66.4 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
||||
44
go.sum
44
go.sum
@@ -237,8 +237,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
@@ -281,8 +281,8 @@ github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLe
|
||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
@@ -297,8 +297,8 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
@@ -597,8 +597,8 @@ go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJ
|
||||
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
|
||||
go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
|
||||
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
@@ -616,8 +616,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -630,8 +630,9 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI=
|
||||
golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
|
||||
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -704,8 +705,8 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -955,11 +956,10 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 h1:YJ5pD9rF8o9Qtta0Cmy9rdBwkSjrTCT6XTiUQVOtIos=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 h1:W5Xj/70xIA4x60O/IFyXivR5MGqblAb8R3w26pnD6No=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8/go.mod h1:vPrPUTsDCYxXWjP7clS81mZ6/803D8K4iM9Ma27VKas=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 h1:mxSlqyb8ZAHsYDCfiXN1EDdNTdvjUJSLY+OnAUtYNYA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@@ -978,8 +978,8 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=
|
||||
google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
|
||||
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
||||
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -993,8 +993,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ=
|
||||
google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
"module_property_set_template_id": "集群模板ID",
|
||||
"module_property_apply_host_property_enabled": "主机属性自动应用",
|
||||
"plat_property_bk_status": "状态",
|
||||
"plat_property_bk_status_detail": "状态详情",
|
||||
"plat_property_bk_cloud_vendor": "云厂商",
|
||||
"plat_property_bk_vpc_id": "VPC唯一标识",
|
||||
"plat_property_bk_vpc_name": "VPC名称",
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"plat_property_bk_cloud_name": "BK-Network Area",
|
||||
"plat_property_bk_supplier_account": "Supplier",
|
||||
"plat_property_bk_status": "Status",
|
||||
"plat_property_bk_status_detail": "Status Detail",
|
||||
"plat_property_bk_cloud_vendor": "Cloud vendor",
|
||||
"plat_property_bk_vpc_id": "VPC ID",
|
||||
"plat_property_bk_vpc_name": "VPC name",
|
||||
|
||||
@@ -779,53 +779,70 @@ func (i IAM) SyncIAMSysInstances(ctx context.Context, redisCli redis.Client, obj
|
||||
// 因为资源间的依赖关系,新建的顺序则反过来为 1.ResourceType 2.InstanceSelection 3.Action
|
||||
// ActionGroup依赖于Action,该资源的增删操作始终放在最后
|
||||
// 先删除资源,再新增资源,因为实例视图的名称在系统中是唯一的,如果不先删,同样名称的实例视图将创建失败
|
||||
options := &SyncRegisterOptions{
|
||||
AddedActions: addedActions,
|
||||
DeletedActions: deletedActions,
|
||||
AddedInstanceSelections: addedInstanceSelections,
|
||||
AddedResourceTypes: addedResourceTypes,
|
||||
DeletedInstanceSelections: deletedInstanceSelections,
|
||||
DeletedResourceTypes: deletedResourceTypes,
|
||||
}
|
||||
|
||||
err = i.deleteIamResources(ctx, deletedActions, deletedInstanceSelections, deletedResourceTypes, rid)
|
||||
return i.syncRegisterActions(ctx, iamResp, objects, options)
|
||||
}
|
||||
|
||||
func (i IAM) syncRegisterActions(ctx context.Context, iamResp *SystemResp, objects []metadata.Object,
|
||||
options *SyncRegisterOptions) error {
|
||||
|
||||
rid := commonutil.ExtractRequestIDFromContext(ctx)
|
||||
err := i.deleteIamResources(ctx, options.DeletedActions, options.DeletedInstanceSelections,
|
||||
options.DeletedResourceTypes, rid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// add cmdb ResourceTypes in iam
|
||||
if len(addedResourceTypes) > 0 {
|
||||
if len(options.AddedResourceTypes) > 0 {
|
||||
blog.Infof("begin add resourceTypes, count:%d, detail:%v, rid: %s",
|
||||
len(addedResourceTypes), addedResourceTypes, rid)
|
||||
if err := i.Client.RegisterResourcesTypes(ctx, addedResourceTypes); err != nil {
|
||||
len(options.AddedResourceTypes), options.AddedResourceTypes, rid)
|
||||
if err := i.Client.RegisterResourcesTypes(ctx, options.AddedResourceTypes); err != nil {
|
||||
blog.ErrorJSON("sync iam sysInstances failed, add resourceType error: %s, resourceType: %s, rid: %s",
|
||||
err, addedResourceTypes, rid)
|
||||
err, options.AddedResourceTypes, rid)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// add cmdb InstanceSelections in iam
|
||||
if len(addedInstanceSelections) > 0 {
|
||||
if len(options.AddedInstanceSelections) > 0 {
|
||||
blog.Infof("begin add instanceSelections, count:%d, detail:%v, rid: %s",
|
||||
len(addedInstanceSelections), addedInstanceSelections, rid)
|
||||
if err := i.Client.RegisterInstanceSelections(ctx, addedInstanceSelections); err != nil {
|
||||
len(options.AddedInstanceSelections), options.AddedInstanceSelections, rid)
|
||||
if err := i.Client.RegisterInstanceSelections(ctx, options.AddedInstanceSelections); err != nil {
|
||||
blog.ErrorJSON("sync iam sysInstances failed, add instanceSelections error: %s, instanceSelections: %s, "+
|
||||
"rid: %s", err, addedInstanceSelections, rid)
|
||||
"rid: %s", err, options.AddedInstanceSelections, rid)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// add cmdb actions in iam
|
||||
if len(addedActions) > 0 {
|
||||
blog.Infof("begin add actions, count:%d, detail:%v, rid: %s", len(addedActions), addedActions, rid)
|
||||
if err := i.Client.RegisterActions(ctx, addedActions); err != nil {
|
||||
if len(options.AddedActions) > 0 {
|
||||
blog.Infof("begin add actions, count:%d, detail:%v, rid: %s", len(options.AddedActions), options.AddedActions,
|
||||
rid)
|
||||
if err := i.Client.RegisterActions(ctx, options.AddedActions); err != nil {
|
||||
blog.ErrorJSON("sync iam sysInstances failed, add IAM actions failed, error: %s, actions: %s, rid: %s",
|
||||
err, addedActions, rid)
|
||||
err, options.AddedActions, rid)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// update action_groups in iam, the action groups contains only the existed actions in iam
|
||||
if len(addedActions) > 0 || len(deletedActions) > 0 {
|
||||
if len(options.AddedActions) > 0 || len(options.DeletedActions) > 0 {
|
||||
actionMap := map[ActionID]struct{}{}
|
||||
for _, action := range iamResp.Data.Actions {
|
||||
if !isIAMSysInstanceAction(action.ID) {
|
||||
actionMap[action.ID] = struct{}{}
|
||||
}
|
||||
}
|
||||
for _, action := range cmdbActions {
|
||||
for _, action := range options.CmdbActions {
|
||||
actionMap[action.ID] = struct{}{}
|
||||
}
|
||||
|
||||
@@ -842,7 +859,6 @@ func (i IAM) SyncIAMSysInstances(ctx context.Context, redisCli redis.Client, obj
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -662,6 +662,17 @@ type RelateResourceType struct {
|
||||
InstanceSelections []RelatedInstanceSelection `json:"related_instance_selections"`
|
||||
}
|
||||
|
||||
// SyncRegisterOptions iam sync register options
|
||||
type SyncRegisterOptions struct {
|
||||
CmdbActions []ResourceAction
|
||||
DeletedActions []ActionID
|
||||
DeletedInstanceSelections []InstanceSelectionID
|
||||
DeletedResourceTypes []TypeID
|
||||
AddedActions []ResourceAction
|
||||
AddedInstanceSelections []InstanceSelection
|
||||
AddedResourceTypes []ResourceType
|
||||
}
|
||||
|
||||
// Scope TODO
|
||||
type Scope struct {
|
||||
Op string `json:"op"`
|
||||
@@ -710,7 +721,7 @@ const (
|
||||
BusinessHostTransferSelection InstanceSelectionID = "business_host_transfer"
|
||||
// BizSetSelection TODO
|
||||
BizSetSelection InstanceSelectionID = "business_set_list"
|
||||
//ProjectSelection project selection
|
||||
// ProjectSelection project selection
|
||||
ProjectSelection InstanceSelectionID = "project"
|
||||
// BizHostInstanceSelection TODO
|
||||
BizHostInstanceSelection InstanceSelectionID = "biz_host_instance"
|
||||
|
||||
@@ -331,31 +331,40 @@ func (ps *parseStream) businessSet() *parseStream {
|
||||
return ps
|
||||
}
|
||||
|
||||
ps = ps.basicBizSetOperations()
|
||||
if ps.shouldReturn() {
|
||||
return ps
|
||||
}
|
||||
|
||||
ps = ps.bizSetListOperations()
|
||||
if ps.shouldReturn() {
|
||||
return ps
|
||||
}
|
||||
|
||||
ps = ps.bizSetTopoOperations()
|
||||
if ps.shouldReturn() {
|
||||
return ps
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
func (ps *parseStream) basicBizSetOperations() *parseStream {
|
||||
if ps.hitPattern(findBizInBizSetPattern, http.MethodPost) {
|
||||
bizSetIDVal, err := ps.RequestCtx.getValueFromBody("bk_biz_set_id")
|
||||
if err != nil {
|
||||
ps.err = err
|
||||
return ps
|
||||
}
|
||||
|
||||
bizSetID := bizSetIDVal.Int()
|
||||
if bizSetID <= 0 {
|
||||
ps.err = errors.New("invalid biz set id")
|
||||
return ps
|
||||
}
|
||||
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{
|
||||
{
|
||||
Basic: meta.Basic{
|
||||
Type: meta.BizSet,
|
||||
Action: meta.AccessBizSet,
|
||||
InstanceID: bizSetID,
|
||||
},
|
||||
},
|
||||
}
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet,
|
||||
Action: meta.AccessBizSet, InstanceID: bizSetID}}}
|
||||
return ps
|
||||
}
|
||||
|
||||
// update biz set, authorize if user has update permission to all of the specified biz set ids
|
||||
if ps.hitPattern(updateBizSetPattern, http.MethodPut) {
|
||||
bizSetIDsVal, err := ps.RequestCtx.getValueFromBody("bk_biz_set_ids")
|
||||
@@ -363,40 +372,28 @@ func (ps *parseStream) businessSet() *parseStream {
|
||||
ps.err = err
|
||||
return ps
|
||||
}
|
||||
|
||||
bizSetIDArr := bizSetIDsVal.Array()
|
||||
if len(bizSetIDArr) == 0 {
|
||||
ps.err = errors.New("bk_biz_set_ids is not set")
|
||||
return ps
|
||||
}
|
||||
|
||||
for _, bizSetIDVal := range bizSetIDArr {
|
||||
bizSetID := bizSetIDVal.Int()
|
||||
if bizSetID <= 0 {
|
||||
ps.err = errors.New("invalid biz set id")
|
||||
return ps
|
||||
}
|
||||
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{
|
||||
{
|
||||
Basic: meta.Basic{
|
||||
Type: meta.BizSet,
|
||||
Action: meta.Update,
|
||||
InstanceID: bizSetID,
|
||||
},
|
||||
},
|
||||
}
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet, Action: meta.Update,
|
||||
InstanceID: bizSetID}}}
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
if ps.hitPattern(deleteBizSetPattern, http.MethodPost) {
|
||||
bizSetIDsVal, err := ps.RequestCtx.getValueFromBody("bk_biz_set_ids")
|
||||
if err != nil {
|
||||
ps.err = err
|
||||
return ps
|
||||
}
|
||||
|
||||
bizSetIDArr := bizSetIDsVal.Array()
|
||||
if len(bizSetIDArr) == 0 {
|
||||
ps.err = errors.New("bk_biz_set_ids is not set")
|
||||
@@ -406,52 +403,32 @@ func (ps *parseStream) businessSet() *parseStream {
|
||||
ps.err = errors.New("bk_biz_set_ids exceeds maximum length 100")
|
||||
return ps
|
||||
}
|
||||
|
||||
for _, bizSetIDVal := range bizSetIDArr {
|
||||
bizSetID := bizSetIDVal.Int()
|
||||
if bizSetID <= 0 {
|
||||
ps.err = errors.New("invalid biz set id")
|
||||
return ps
|
||||
}
|
||||
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{
|
||||
{
|
||||
Basic: meta.Basic{
|
||||
Type: meta.BizSet,
|
||||
Action: meta.Delete,
|
||||
InstanceID: bizSetID,
|
||||
},
|
||||
},
|
||||
}
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet, Action: meta.Delete,
|
||||
InstanceID: bizSetID}}}
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
if ps.hitPattern(findBizSetTopoPattern, http.MethodPost) {
|
||||
bizSetIDVal, err := ps.RequestCtx.getValueFromBody("bk_biz_set_id")
|
||||
if err != nil {
|
||||
ps.err = err
|
||||
return ps
|
||||
}
|
||||
|
||||
bizSetID := bizSetIDVal.Int()
|
||||
if bizSetID <= 0 {
|
||||
ps.err = errors.New("invalid biz set id")
|
||||
return ps
|
||||
}
|
||||
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{
|
||||
{
|
||||
Basic: meta.Basic{
|
||||
Type: meta.BizSet,
|
||||
Action: meta.AccessBizSet,
|
||||
InstanceID: bizSetID,
|
||||
},
|
||||
},
|
||||
}
|
||||
// create business set, this is not a normalize api.
|
||||
if ps.hitPattern(createBizSetPattern, http.MethodPost) {
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet, Action: meta.Create}}}
|
||||
return ps
|
||||
}
|
||||
// preview business set
|
||||
if ps.hitPattern(previewBusinessSetPattern, http.MethodPost) {
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet,
|
||||
Action: meta.SkipAction}}}
|
||||
return ps
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
func (ps *parseStream) bizSetListOperations() *parseStream {
|
||||
// find many business set list for the user with any business set resources
|
||||
if ps.hitPattern(findmanyBusinessSetPattern, http.MethodPost) {
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{
|
||||
@@ -490,133 +467,81 @@ func (ps *parseStream) businessSet() *parseStream {
|
||||
}
|
||||
return ps
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
// create business set, this is not a normalize api.
|
||||
if ps.hitPattern(createBizSetPattern, http.MethodPost) {
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{
|
||||
{
|
||||
Basic: meta.Basic{
|
||||
Type: meta.BizSet,
|
||||
Action: meta.Create,
|
||||
},
|
||||
},
|
||||
func (ps *parseStream) bizSetTopoOperations() *parseStream {
|
||||
if ps.hitPattern(findBizSetTopoPattern, http.MethodPost) {
|
||||
bizSetIDVal, err := ps.RequestCtx.getValueFromBody("bk_biz_set_id")
|
||||
if err != nil {
|
||||
ps.err = err
|
||||
return ps
|
||||
}
|
||||
bizSetID := bizSetIDVal.Int()
|
||||
if bizSetID <= 0 {
|
||||
ps.err = errors.New("invalid biz set id")
|
||||
return ps
|
||||
}
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet,
|
||||
Action: meta.AccessBizSet, InstanceID: bizSetID}}}
|
||||
return ps
|
||||
}
|
||||
|
||||
// preview business set
|
||||
if ps.hitPattern(previewBusinessSetPattern, http.MethodPost) {
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{
|
||||
{
|
||||
Basic: meta.Basic{
|
||||
Type: meta.BizSet,
|
||||
Action: meta.SkipAction,
|
||||
},
|
||||
},
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
if ps.hitRegexp(listSetInBizSetRegexp, http.MethodPost) {
|
||||
if len(ps.RequestCtx.Elements) != 8 {
|
||||
ps.err = fmt.Errorf("get invalid url elements length %d", len(ps.RequestCtx.Elements))
|
||||
return ps
|
||||
}
|
||||
|
||||
bizSetID, err := strconv.ParseInt(ps.RequestCtx.Elements[5], 10, 64)
|
||||
if err != nil {
|
||||
ps.err = fmt.Errorf("get invalid business set id %s, err: %v", ps.RequestCtx.Elements[5], err)
|
||||
return ps
|
||||
}
|
||||
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{
|
||||
{
|
||||
Basic: meta.Basic{
|
||||
Type: meta.BizSet,
|
||||
Action: meta.AccessBizSet,
|
||||
InstanceID: bizSetID,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet,
|
||||
Action: meta.AccessBizSet, InstanceID: bizSetID}}}
|
||||
return ps
|
||||
}
|
||||
|
||||
if ps.hitRegexp(listModuleInBizSetRegexp, http.MethodPost) {
|
||||
if len(ps.RequestCtx.Elements) != 10 {
|
||||
ps.err = fmt.Errorf("get invalid url elements length %d", len(ps.RequestCtx.Elements))
|
||||
return ps
|
||||
}
|
||||
|
||||
bizSetID, err := strconv.ParseInt(ps.RequestCtx.Elements[5], 10, 64)
|
||||
if err != nil {
|
||||
ps.err = fmt.Errorf("get invalid business set id %s, err: %v", ps.RequestCtx.Elements[5], err)
|
||||
return ps
|
||||
}
|
||||
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{
|
||||
{
|
||||
Basic: meta.Basic{
|
||||
Type: meta.BizSet,
|
||||
Action: meta.AccessBizSet,
|
||||
InstanceID: bizSetID,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet,
|
||||
Action: meta.AccessBizSet, InstanceID: bizSetID}}}
|
||||
return ps
|
||||
}
|
||||
|
||||
if ps.hitRegexp(findBizSetTopoPathRegexp, http.MethodPost) {
|
||||
if len(ps.RequestCtx.Elements) != 8 {
|
||||
ps.err = fmt.Errorf("get invalid url elements length %d", len(ps.RequestCtx.Elements))
|
||||
return ps
|
||||
}
|
||||
|
||||
bizSetID, err := strconv.ParseInt(ps.RequestCtx.Elements[5], 10, 64)
|
||||
if err != nil {
|
||||
ps.err = fmt.Errorf("get invalid business set id %s, err: %v", ps.RequestCtx.Elements[5], err)
|
||||
return ps
|
||||
}
|
||||
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{
|
||||
{
|
||||
Basic: meta.Basic{
|
||||
Type: meta.BizSet,
|
||||
Action: meta.AccessBizSet,
|
||||
InstanceID: bizSetID,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet,
|
||||
Action: meta.AccessBizSet, InstanceID: bizSetID}}}
|
||||
return ps
|
||||
}
|
||||
|
||||
if ps.hitRegexp(countTopoHostAndSrvRegexp, http.MethodPost) {
|
||||
if len(ps.RequestCtx.Elements) != 7 {
|
||||
ps.err = fmt.Errorf("get invalid url elements length %d", len(ps.RequestCtx.Elements))
|
||||
return ps
|
||||
}
|
||||
|
||||
bizSetID, err := strconv.ParseInt(ps.RequestCtx.Elements[6], 10, 64)
|
||||
if err != nil {
|
||||
ps.err = fmt.Errorf("get invalid business set id %s, err: %v", ps.RequestCtx.Elements[6], err)
|
||||
return ps
|
||||
}
|
||||
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{
|
||||
{
|
||||
Basic: meta.Basic{
|
||||
Type: meta.BizSet,
|
||||
Action: meta.AccessBizSet,
|
||||
InstanceID: bizSetID,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet,
|
||||
Action: meta.AccessBizSet, InstanceID: bizSetID}}}
|
||||
return ps
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
|
||||
@@ -78,8 +78,9 @@ func (h *host) TransferToNormalModule(ctx context.Context, header http.Header, i
|
||||
// 如果主机属于空闲机模块,操作失败
|
||||
// 如果主机属于故障机模块,操作失败
|
||||
// 如果主机不在参数指定的模块中,操作失败
|
||||
func (h *host) RemoveFromModule(ctx context.Context, header http.Header,
|
||||
input *metadata.RemoveHostsFromModuleOption) (resp *metadata.OperaterException, err error) {
|
||||
func (h *host) RemoveFromModule(ctx context.Context, header http.Header, input *metadata.RemoveHostsFromModuleOption) (
|
||||
resp *metadata.OperaterException, err error) {
|
||||
|
||||
resp = new(metadata.OperaterException)
|
||||
subPath := "/delete/host/host_module_relations"
|
||||
|
||||
@@ -96,6 +97,7 @@ func (h *host) RemoveFromModule(ctx context.Context, header http.Header,
|
||||
// TransferToAnotherBusiness transfer host to other business module
|
||||
func (h *host) TransferToAnotherBusiness(ctx context.Context, header http.Header,
|
||||
input *metadata.TransferHostsCrossBusinessRequest) (resp *metadata.OperaterException, err error) {
|
||||
|
||||
resp = new(metadata.OperaterException)
|
||||
subPath := "/set/module/host/relation/cross/business"
|
||||
|
||||
@@ -186,6 +188,7 @@ func (h *host) FindIdentifier(ctx context.Context, header http.Header, input *me
|
||||
// GetHostByID TODO
|
||||
func (h *host) GetHostByID(ctx context.Context, header http.Header, hostID int64) (resp *metadata.HostInstanceResult,
|
||||
err error) {
|
||||
|
||||
resp = new(metadata.HostInstanceResult)
|
||||
subPath := "/find/host/%d"
|
||||
|
||||
@@ -224,8 +227,9 @@ func (h *host) GetHosts(ctx context.Context, header http.Header, opt *metadata.Q
|
||||
}
|
||||
|
||||
// LockHost TODO
|
||||
func (h *host) LockHost(ctx context.Context, header http.Header,
|
||||
input *metadata.HostLockRequest) (resp *metadata.HostLockResponse, err error) {
|
||||
func (h *host) LockHost(ctx context.Context, header http.Header, input *metadata.HostLockRequest) (
|
||||
resp *metadata.HostLockResponse, err error) {
|
||||
|
||||
resp = new(metadata.HostLockResponse)
|
||||
subPath := "/find/host/lock"
|
||||
|
||||
@@ -240,8 +244,9 @@ func (h *host) LockHost(ctx context.Context, header http.Header,
|
||||
}
|
||||
|
||||
// UnlockHost TODO
|
||||
func (h *host) UnlockHost(ctx context.Context, header http.Header,
|
||||
input *metadata.HostLockRequest) (resp *metadata.HostLockResponse, err error) {
|
||||
func (h *host) UnlockHost(ctx context.Context, header http.Header, input *metadata.HostLockRequest) (
|
||||
resp *metadata.HostLockResponse, err error) {
|
||||
|
||||
resp = new(metadata.HostLockResponse)
|
||||
subPath := "/delete/host/lock"
|
||||
|
||||
@@ -256,8 +261,9 @@ func (h *host) UnlockHost(ctx context.Context, header http.Header,
|
||||
}
|
||||
|
||||
// QueryHostLock TODO
|
||||
func (h *host) QueryHostLock(ctx context.Context, header http.Header,
|
||||
input *metadata.QueryHostLockRequest) (resp *metadata.HostLockQueryResponse, err error) {
|
||||
func (h *host) QueryHostLock(ctx context.Context, header http.Header, input *metadata.QueryHostLockRequest) (
|
||||
resp *metadata.HostLockQueryResponse, err error) {
|
||||
|
||||
resp = new(metadata.HostLockQueryResponse)
|
||||
subPath := "/findmany/host/lock/search"
|
||||
|
||||
@@ -272,8 +278,8 @@ func (h *host) QueryHostLock(ctx context.Context, header http.Header,
|
||||
}
|
||||
|
||||
// CreateDynamicGroup is dynamic group query datas base on conditions action api machinery.
|
||||
func (h *host) CreateDynamicGroup(ctx context.Context, header http.Header,
|
||||
data *metadata.DynamicGroup) (resp *metadata.IDResult, err error) {
|
||||
func (h *host) CreateDynamicGroup(ctx context.Context, header http.Header, data *metadata.DynamicGroup) (
|
||||
resp *metadata.IDResult, err error) {
|
||||
|
||||
resp = new(metadata.IDResult)
|
||||
subPath := "/create/dynamicgroup"
|
||||
@@ -289,8 +295,8 @@ func (h *host) CreateDynamicGroup(ctx context.Context, header http.Header,
|
||||
}
|
||||
|
||||
// UpdateDynamicGroup is dynamic group update action api machinery.
|
||||
func (h *host) UpdateDynamicGroup(ctx context.Context, bizID, id string,
|
||||
header http.Header, data map[string]interface{}) (resp *metadata.BaseResp, err error) {
|
||||
func (h *host) UpdateDynamicGroup(ctx context.Context, bizID, id string, header http.Header,
|
||||
data map[string]interface{}) (resp *metadata.BaseResp, err error) {
|
||||
|
||||
resp = new(metadata.BaseResp)
|
||||
subPath := "/update/dynamicgroup/%s/%s"
|
||||
@@ -306,8 +312,8 @@ func (h *host) UpdateDynamicGroup(ctx context.Context, bizID, id string,
|
||||
}
|
||||
|
||||
// DeleteDynamicGroup is dynamic group delete action api machinery.
|
||||
func (h *host) DeleteDynamicGroup(ctx context.Context, bizID, id string,
|
||||
header http.Header) (resp *metadata.BaseResp, err error) {
|
||||
func (h *host) DeleteDynamicGroup(ctx context.Context, bizID, id string, header http.Header) (
|
||||
resp *metadata.BaseResp, err error) {
|
||||
|
||||
resp = new(metadata.BaseResp)
|
||||
subPath := "/delete/dynamicgroup/%s/%s"
|
||||
@@ -323,8 +329,8 @@ func (h *host) DeleteDynamicGroup(ctx context.Context, bizID, id string,
|
||||
}
|
||||
|
||||
// GetDynamicGroup is dynamic group query detail action api machinery.
|
||||
func (h *host) GetDynamicGroup(ctx context.Context, bizID, id string,
|
||||
header http.Header) (resp *metadata.GetDynamicGroupResult, err error) {
|
||||
func (h *host) GetDynamicGroup(ctx context.Context, bizID, id string, header http.Header) (
|
||||
resp *metadata.GetDynamicGroupResult, err error) {
|
||||
|
||||
resp = new(metadata.GetDynamicGroupResult)
|
||||
subPath := "/find/dynamicgroup/%s/%s"
|
||||
@@ -340,8 +346,8 @@ func (h *host) GetDynamicGroup(ctx context.Context, bizID, id string,
|
||||
}
|
||||
|
||||
// SearchDynamicGroup is dynamic group search action api machinery.
|
||||
func (h *host) SearchDynamicGroup(ctx context.Context, header http.Header,
|
||||
opt *metadata.QueryCondition) (resp *metadata.SearchDynamicGroupResult, err error) {
|
||||
func (h *host) SearchDynamicGroup(ctx context.Context, header http.Header, opt *metadata.QueryCondition) (
|
||||
resp *metadata.SearchDynamicGroupResult, err error) {
|
||||
|
||||
resp = new(metadata.SearchDynamicGroupResult)
|
||||
subPath := "/findmany/dynamicgroup/search"
|
||||
@@ -357,8 +363,9 @@ func (h *host) SearchDynamicGroup(ctx context.Context, header http.Header,
|
||||
}
|
||||
|
||||
// AddUserCustom TODO
|
||||
func (h *host) AddUserCustom(ctx context.Context, user string, header http.Header,
|
||||
dat map[string]interface{}) (resp *metadata.BaseResp, err error) {
|
||||
func (h *host) AddUserCustom(ctx context.Context, user string, header http.Header, dat map[string]interface{}) (
|
||||
resp *metadata.BaseResp, err error) {
|
||||
|
||||
resp = new(metadata.BaseResp)
|
||||
subPath := "/create/usercustom/%s"
|
||||
|
||||
@@ -375,6 +382,7 @@ func (h *host) AddUserCustom(ctx context.Context, user string, header http.Heade
|
||||
// UpdateUserCustomByID TODO
|
||||
func (h *host) UpdateUserCustomByID(ctx context.Context, user string, id string, header http.Header,
|
||||
dat map[string]interface{}) (resp *metadata.BaseResp, err error) {
|
||||
|
||||
resp = new(metadata.BaseResp)
|
||||
subPath := "/update/usercustom/%s/%s"
|
||||
|
||||
@@ -389,8 +397,9 @@ func (h *host) UpdateUserCustomByID(ctx context.Context, user string, id string,
|
||||
}
|
||||
|
||||
// GetUserCustomByUser TODO
|
||||
func (h *host) GetUserCustomByUser(ctx context.Context, user string,
|
||||
header http.Header) (resp *metadata.GetUserCustomResult, err error) {
|
||||
func (h *host) GetUserCustomByUser(ctx context.Context, user string, header http.Header) (
|
||||
resp *metadata.GetUserCustomResult, err error) {
|
||||
|
||||
resp = new(metadata.GetUserCustomResult)
|
||||
subPath := "/find/usercustom/user/search/%s"
|
||||
|
||||
@@ -406,6 +415,7 @@ func (h *host) GetUserCustomByUser(ctx context.Context, user string,
|
||||
// GetDefaultUserCustom TODO
|
||||
func (h *host) GetDefaultUserCustom(ctx context.Context, header http.Header) (resp *metadata.GetUserCustomResult,
|
||||
err error) {
|
||||
|
||||
resp = new(metadata.GetUserCustomResult)
|
||||
subPath := "/find/usercustom/default"
|
||||
|
||||
@@ -420,8 +430,9 @@ func (h *host) GetDefaultUserCustom(ctx context.Context, header http.Header) (re
|
||||
}
|
||||
|
||||
// UpdateDefaultUserCustom TODO
|
||||
func (h *host) UpdateDefaultUserCustom(ctx context.Context, header http.Header,
|
||||
dat map[string]interface{}) (resp *metadata.BaseResp, err error) {
|
||||
func (h *host) UpdateDefaultUserCustom(ctx context.Context, header http.Header, dat map[string]interface{}) (
|
||||
resp *metadata.BaseResp, err error) {
|
||||
|
||||
resp = new(metadata.BaseResp)
|
||||
|
||||
err = h.client.Put().
|
||||
@@ -435,8 +446,9 @@ func (h *host) UpdateDefaultUserCustom(ctx context.Context, header http.Header,
|
||||
}
|
||||
|
||||
// AddHostFavourite TODO
|
||||
func (h *host) AddHostFavourite(ctx context.Context, user string, header http.Header,
|
||||
dat *metadata.FavouriteParms) (resp *metadata.IDResult, err error) {
|
||||
func (h *host) AddHostFavourite(ctx context.Context, user string, header http.Header, dat *metadata.FavouriteParms) (
|
||||
resp *metadata.IDResult, err error) {
|
||||
|
||||
resp = new(metadata.IDResult)
|
||||
subPath := "/create/hosts/favorites/%s"
|
||||
|
||||
@@ -453,6 +465,7 @@ func (h *host) AddHostFavourite(ctx context.Context, user string, header http.He
|
||||
// UpdateHostFavouriteByID TODO
|
||||
func (h *host) UpdateHostFavouriteByID(ctx context.Context, user string, id string, header http.Header,
|
||||
dat map[string]interface{}) (resp *metadata.BaseResp, err error) {
|
||||
|
||||
resp = new(metadata.BaseResp)
|
||||
subPath := "/update/hosts/favorites/%s/%s"
|
||||
|
||||
@@ -467,8 +480,9 @@ func (h *host) UpdateHostFavouriteByID(ctx context.Context, user string, id stri
|
||||
}
|
||||
|
||||
// DeleteHostFavouriteByID TODO
|
||||
func (h *host) DeleteHostFavouriteByID(ctx context.Context, user string, id string,
|
||||
header http.Header) (resp *metadata.BaseResp, err error) {
|
||||
func (h *host) DeleteHostFavouriteByID(ctx context.Context, user string, id string, header http.Header) (
|
||||
resp *metadata.BaseResp, err error) {
|
||||
|
||||
resp = new(metadata.BaseResp)
|
||||
subPath := "/delete/hosts/favorites/%s/%s"
|
||||
|
||||
@@ -483,8 +497,9 @@ func (h *host) DeleteHostFavouriteByID(ctx context.Context, user string, id stri
|
||||
}
|
||||
|
||||
// ListHostFavourites TODO
|
||||
func (h *host) ListHostFavourites(ctx context.Context, user string, header http.Header,
|
||||
dat *metadata.QueryInput) (resp *metadata.GetHostFavoriteResult, err error) {
|
||||
func (h *host) ListHostFavourites(ctx context.Context, user string, header http.Header, dat *metadata.QueryInput) (
|
||||
resp *metadata.GetHostFavoriteResult, err error) {
|
||||
|
||||
resp = new(metadata.GetHostFavoriteResult)
|
||||
subPath := "/findmany/hosts/favorites/search/%s"
|
||||
|
||||
@@ -499,8 +514,9 @@ func (h *host) ListHostFavourites(ctx context.Context, user string, header http.
|
||||
}
|
||||
|
||||
// GetHostFavouriteByID TODO
|
||||
func (h *host) GetHostFavouriteByID(ctx context.Context, user string, id string,
|
||||
header http.Header) (resp *metadata.GetHostFavoriteWithIDResult, err error) {
|
||||
func (h *host) GetHostFavouriteByID(ctx context.Context, user string, id string, header http.Header) (
|
||||
resp *metadata.GetHostFavoriteWithIDResult, err error) {
|
||||
|
||||
resp = new(metadata.GetHostFavoriteWithIDResult)
|
||||
subPath := "/find/hosts/favorites/search/%s/%s"
|
||||
|
||||
@@ -514,8 +530,9 @@ func (h *host) GetHostFavouriteByID(ctx context.Context, user string, id string,
|
||||
}
|
||||
|
||||
// GetHostModulesIDs TODO
|
||||
func (h *host) GetHostModulesIDs(ctx context.Context, header http.Header,
|
||||
dat *metadata.ModuleHostConfigParams) (resp *metadata.GetHostModuleIDsResult, err error) {
|
||||
func (h *host) GetHostModulesIDs(ctx context.Context, header http.Header, dat *metadata.ModuleHostConfigParams) (
|
||||
resp *metadata.GetHostModuleIDsResult, err error) {
|
||||
|
||||
resp = new(metadata.GetHostModuleIDsResult)
|
||||
subPath := "/findmany/meta/hosts/modules/search"
|
||||
|
||||
@@ -580,8 +597,9 @@ func (h *host) UpdateHostCloudAreaField(ctx context.Context, header http.Header,
|
||||
}
|
||||
|
||||
// FindCloudAreaHostCount TODO
|
||||
func (h *host) FindCloudAreaHostCount(ctx context.Context, header http.Header,
|
||||
option metadata.CloudAreaHostCount) (resp *metadata.CloudAreaHostCountResult, err error) {
|
||||
func (h *host) FindCloudAreaHostCount(ctx context.Context, header http.Header, option metadata.CloudAreaHostCount) (
|
||||
resp *metadata.CloudAreaHostCountResult, err error) {
|
||||
|
||||
resp = new(metadata.CloudAreaHostCountResult)
|
||||
subPath := "/findmany/cloudarea/hostcount"
|
||||
|
||||
|
||||
@@ -8,8 +8,9 @@ import (
|
||||
)
|
||||
|
||||
// SearchInstCount TODO
|
||||
func (s *operation) SearchInstCount(ctx context.Context, h http.Header,
|
||||
data interface{}) (resp *metadata.CoreUint64Response, err error) {
|
||||
func (s *operation) SearchInstCount(ctx context.Context, h http.Header, data interface{}) (
|
||||
resp *metadata.CoreUint64Response, err error) {
|
||||
|
||||
resp = new(metadata.CoreUint64Response)
|
||||
subPath := "/find/operation/inst/count"
|
||||
|
||||
@@ -24,8 +25,9 @@ func (s *operation) SearchInstCount(ctx context.Context, h http.Header,
|
||||
}
|
||||
|
||||
// SearchChartData TODO
|
||||
func (s *operation) SearchChartData(ctx context.Context, h http.Header,
|
||||
data metadata.ChartConfig) (resp *metadata.Response, err error) {
|
||||
func (s *operation) SearchChartData(ctx context.Context, h http.Header, data metadata.ChartConfig) (
|
||||
resp *metadata.Response, err error) {
|
||||
|
||||
resp = new(metadata.Response)
|
||||
subPath := "/find/operation/chart/data"
|
||||
|
||||
@@ -40,8 +42,9 @@ func (s *operation) SearchChartData(ctx context.Context, h http.Header,
|
||||
}
|
||||
|
||||
// CreateOperationChart TODO
|
||||
func (s *operation) CreateOperationChart(ctx context.Context, h http.Header,
|
||||
data interface{}) (resp *metadata.CoreUint64Response, err error) {
|
||||
func (s *operation) CreateOperationChart(ctx context.Context, h http.Header, data interface{}) (
|
||||
resp *metadata.CoreUint64Response, err error) {
|
||||
|
||||
resp = new(metadata.CoreUint64Response)
|
||||
subPath := "/create/operation/chart"
|
||||
|
||||
@@ -58,6 +61,7 @@ func (s *operation) CreateOperationChart(ctx context.Context, h http.Header,
|
||||
// DeleteOperationChart TODO
|
||||
func (s *operation) DeleteOperationChart(ctx context.Context, h http.Header, id string) (resp *metadata.Response,
|
||||
err error) {
|
||||
|
||||
resp = new(metadata.Response)
|
||||
subPath := "delete/operation/chart/%v"
|
||||
|
||||
@@ -72,8 +76,9 @@ func (s *operation) DeleteOperationChart(ctx context.Context, h http.Header, id
|
||||
}
|
||||
|
||||
// SearchOperationCharts TODO
|
||||
func (s *operation) SearchOperationCharts(ctx context.Context, h http.Header,
|
||||
data interface{}) (resp *metadata.SearchChartResponse, err error) {
|
||||
func (s *operation) SearchOperationCharts(ctx context.Context, h http.Header, data interface{}) (
|
||||
resp *metadata.SearchChartResponse, err error) {
|
||||
|
||||
resp = new(metadata.SearchChartResponse)
|
||||
subPath := "/findmany/operation/chart"
|
||||
|
||||
@@ -90,6 +95,7 @@ func (s *operation) SearchOperationCharts(ctx context.Context, h http.Header,
|
||||
// UpdateOperationChart TODO
|
||||
func (s *operation) UpdateOperationChart(ctx context.Context, h http.Header, data interface{}) (resp *metadata.Response,
|
||||
err error) {
|
||||
|
||||
resp = new(metadata.Response)
|
||||
subPath := "update/operation/chart"
|
||||
|
||||
@@ -106,6 +112,7 @@ func (s *operation) UpdateOperationChart(ctx context.Context, h http.Header, dat
|
||||
// SearchTimerChartData TODO
|
||||
func (s *operation) SearchTimerChartData(ctx context.Context, h http.Header, data interface{}) (resp *metadata.Response,
|
||||
err error) {
|
||||
|
||||
resp = new(metadata.Response)
|
||||
subPath := "/find/operation/timer/chart/data"
|
||||
|
||||
@@ -122,6 +129,7 @@ func (s *operation) SearchTimerChartData(ctx context.Context, h http.Header, dat
|
||||
// UpdateChartPosition TODO
|
||||
func (s *operation) UpdateChartPosition(ctx context.Context, h http.Header, data interface{}) (resp *metadata.Response,
|
||||
err error) {
|
||||
|
||||
resp = new(metadata.Response)
|
||||
subPath := "/update/operation/chart/position"
|
||||
|
||||
@@ -136,8 +144,9 @@ func (s *operation) UpdateChartPosition(ctx context.Context, h http.Header, data
|
||||
}
|
||||
|
||||
// SearchChartCommon TODO
|
||||
func (s *operation) SearchChartCommon(ctx context.Context, h http.Header,
|
||||
data interface{}) (resp *metadata.SearchChartCommon, err error) {
|
||||
func (s *operation) SearchChartCommon(ctx context.Context, h http.Header, data interface{}) (
|
||||
resp *metadata.SearchChartCommon, err error) {
|
||||
|
||||
resp = new(metadata.SearchChartCommon)
|
||||
subPath := "/find/operation/chart/common"
|
||||
|
||||
@@ -154,6 +163,7 @@ func (s *operation) SearchChartCommon(ctx context.Context, h http.Header,
|
||||
// TimerFreshData TODO
|
||||
func (s *operation) TimerFreshData(ctx context.Context, h http.Header, data interface{}) (resp *metadata.BoolResponse,
|
||||
err error) {
|
||||
|
||||
resp = new(metadata.BoolResponse)
|
||||
subPath := "/start/operation/chart/timer"
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ type ServiceClientInterface interface {
|
||||
|
||||
CreateServiceInstance(ctx context.Context, h http.Header, data *metadata.CreateServiceInstanceInput) ([]int64,
|
||||
errors.CCErrorCoder)
|
||||
UpdateServiceInstances(ctx context.Context, h http.Header, bizID int64,
|
||||
data map[string]interface{}) (resp *metadata.Response, err error)
|
||||
UpdateServiceInstances(ctx context.Context, h http.Header, bizID int64, data map[string]interface{}) (
|
||||
resp *metadata.Response, err error)
|
||||
DeleteServiceInstance(ctx context.Context, h http.Header, data map[string]interface{}) (resp *metadata.Response,
|
||||
err error)
|
||||
SearchServiceInstance(ctx context.Context, h http.Header, data *metadata.GetServiceInstanceInModuleInput) (
|
||||
@@ -32,13 +32,13 @@ type ServiceClientInterface interface {
|
||||
data map[string]interface{}) (resp *metadata.ResponseInstData, err error)
|
||||
ServiceInstanceAddLabels(ctx context.Context, h http.Header, data map[string]interface{}) (resp *metadata.Response,
|
||||
err error)
|
||||
ServiceInstanceRemoveLabels(ctx context.Context, h http.Header,
|
||||
data map[string]interface{}) (resp *metadata.Response, err error)
|
||||
ServiceInstanceRemoveLabels(ctx context.Context, h http.Header, data map[string]interface{}) (
|
||||
resp *metadata.Response, err error)
|
||||
ServiceInstanceFindLabels(ctx context.Context, h http.Header, data map[string]interface{}) (resp *metadata.Response,
|
||||
err error)
|
||||
|
||||
CreateServiceTemplate(ctx context.Context, h http.Header,
|
||||
data map[string]interface{}) (resp *metadata.ResponseDataMapStr, err error)
|
||||
CreateServiceTemplate(ctx context.Context, h http.Header, data map[string]interface{}) (
|
||||
resp *metadata.ResponseDataMapStr, err error)
|
||||
DeleteServiceTemplate(ctx context.Context, h http.Header,
|
||||
input *metadata.DeleteServiceTemplatesInput) errors.CCErrorCoder
|
||||
SearchServiceTemplate(ctx context.Context, h http.Header,
|
||||
|
||||
@@ -390,24 +390,14 @@ func (s *service) LimiterFilter() func(req *restful.Request, resp *restful.Respo
|
||||
return
|
||||
}
|
||||
|
||||
key := common.ApiCacheLimiterRulePrefix + rule.RuleName
|
||||
result, err := s.cache.Eval(context.Background(), setRequestCntTTLScript, []string{key}, rule.TTL).Result()
|
||||
exceedLimit, err := s.checkIfExceedLimit(rule, rid)
|
||||
if err != nil {
|
||||
blog.Errorf("redis Eval failed, key:%s, rule:%#v, err: %v, rid: %s", key, *rule, err, rid)
|
||||
fchain.ProcessFilter(req, resp)
|
||||
return
|
||||
}
|
||||
cnt, ok := result.(int64)
|
||||
if !ok {
|
||||
blog.Errorf("execute setRequestCntTTLScript failed, key:%s, rule:%#v, err: %v, rid: %s",
|
||||
key, *rule, result, rid)
|
||||
fchain.ProcessFilter(req, resp)
|
||||
return
|
||||
}
|
||||
|
||||
if cnt > rule.Limit {
|
||||
if exceedLimit {
|
||||
blog.Errorf("too many requests, matched rule is %#v, rid: %s", *rule, rid)
|
||||
|
||||
s.errorLimiterTotal.With(prometheus.Labels{
|
||||
metrics.LabelAppCode: httpheader.GetAppCode(req.Request.Header),
|
||||
metrics.LabelHandler: req.Request.RequestURI,
|
||||
@@ -427,6 +417,29 @@ func (s *service) LimiterFilter() func(req *restful.Request, resp *restful.Respo
|
||||
}
|
||||
}
|
||||
|
||||
// checkIfExceedLimit checks if the request exceeds the rate limit, return true if request should be rejected
|
||||
func (s *service) checkIfExceedLimit(rule *metadata.LimiterRule, rid string) (bool, error) {
|
||||
|
||||
key := common.ApiCacheLimiterRulePrefix + rule.RuleName
|
||||
result, err := s.cache.Eval(context.Background(), setRequestCntTTLScript, []string{key}, rule.TTL).Result()
|
||||
if err != nil {
|
||||
blog.Errorf("redis Eval failed, key:%s, rule:%#v, err: %v, rid: %s", key, *rule, err, rid)
|
||||
return false, err
|
||||
}
|
||||
|
||||
cnt, ok := result.(int64)
|
||||
if !ok {
|
||||
blog.Errorf("execute setRequestCntTTLScript failed, key:%s, rule:%#v, result: %v, rid: %s",
|
||||
key, *rule, result, rid)
|
||||
return false, fmt.Errorf("execute setRequestCntTTLScript failed, result: %v", result)
|
||||
}
|
||||
|
||||
if cnt > rule.Limit {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// JwtFilter the filter that handles the source of the jwt request
|
||||
func (s *service) JwtFilter() func(req *restful.Request, resp *restful.Response, fchain *restful.FilterChain) {
|
||||
return func(req *restful.Request, resp *restful.Response, fchain *restful.FilterChain) {
|
||||
|
||||
@@ -112,8 +112,12 @@ func SetRid(header http.Header, value string) {
|
||||
|
||||
// SetBkAuth set blueking api gateway authorization info to http header
|
||||
func SetBkAuth(header http.Header, value string) http.Header {
|
||||
header.Set(BkAuthHeader, value)
|
||||
return header
|
||||
h := make(http.Header)
|
||||
for key := range header {
|
||||
h.Set(key, header.Get(key))
|
||||
}
|
||||
h.Set(BkAuthHeader, value)
|
||||
return h
|
||||
}
|
||||
|
||||
// SetBkJWT set blueking api gateway jwt info to http header
|
||||
|
||||
@@ -22,13 +22,13 @@ type SysUserConfigItem struct {
|
||||
ExpireAt int64 `json:"expire_at" bson:"expire_at"`
|
||||
}
|
||||
|
||||
// ResponseSysUserConfigData TODO
|
||||
// ResponseSysUserConfigData response data for sys user config
|
||||
type ResponseSysUserConfigData struct {
|
||||
RowType string `json:"type"`
|
||||
BluekingModify SysUserConfigItem `json:"blueking_modify"`
|
||||
}
|
||||
|
||||
// ReponseSysUserConfig TODO
|
||||
// ReponseSysUserConfig response for sys user config
|
||||
type ReponseSysUserConfig struct {
|
||||
BaseResp `json:",inline"`
|
||||
Data ResponseSysUserConfigData `json:"data"`
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"configcenter/src/common/errors"
|
||||
"configcenter/src/common/mapstr"
|
||||
"configcenter/src/common/util"
|
||||
"configcenter/src/common/valid/attribute/manager"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
@@ -174,6 +175,7 @@ type CreateObjAttDesResp struct {
|
||||
|
||||
// Validate Attribute
|
||||
func (attribute *Attribute) Validate(ctx context.Context, data interface{}, key string) errors.RawErrorInfo {
|
||||
rid := util.ExtractRequestIDFromContext(ctx)
|
||||
var attrValidatorMap = map[string]func(context.Context, interface{}, string) errors.RawErrorInfo{
|
||||
common.FieldTypeSingleChar: attribute.validChar,
|
||||
common.FieldTypeLongChar: attribute.validLongChar,
|
||||
@@ -203,16 +205,29 @@ func (attribute *Attribute) Validate(ctx context.Context, data interface{}, key
|
||||
// TODO what validation should do on these types
|
||||
rawError = attribute.validTable(ctx, data, key)
|
||||
default:
|
||||
// notice: 注意default 这里的实现逻辑, 用break 做了执行流程的终止。 pr 建议,降低圈复杂度
|
||||
|
||||
validator, exists := attrValidatorMap[fieldType]
|
||||
if !exists {
|
||||
rawError = errors.RawErrorInfo{
|
||||
ErrCode: common.CCErrCommUnexpectedFieldType,
|
||||
Args: []interface{}{fieldType},
|
||||
if exists {
|
||||
rawError = validator(ctx, data, key)
|
||||
break
|
||||
}
|
||||
// 是否为扩展字段类型
|
||||
if handle, ok := manager.Get(fieldType); ok {
|
||||
if err := handle.Validate(ctx, key, fieldType, attribute.IsRequired, attribute.Option, data); err != nil {
|
||||
blog.Errorf("validate attribute fail, field type: %s, err: %v, rid: %s", fieldType, err, rid)
|
||||
return errors.RawErrorInfo{
|
||||
ErrCode: common.CCErrCommParamsInvalid,
|
||||
Args: []interface{}{err.Error()},
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
rawError = errors.RawErrorInfo{
|
||||
ErrCode: common.CCErrCommUnexpectedFieldType,
|
||||
Args: []interface{}{fieldType},
|
||||
}
|
||||
|
||||
rawError = validator(ctx, data, key)
|
||||
}
|
||||
// 如果出现了问题,并且报错原内容为propertyID,则替换为propertyName。
|
||||
if rawError.ErrCode != 0 {
|
||||
|
||||
@@ -39,12 +39,12 @@ type AuditQueryResult struct {
|
||||
Info []AuditLog `json:"info"`
|
||||
}
|
||||
|
||||
// CreateAuditLogParam TODO
|
||||
// CreateAuditLogParam param for creating audit log
|
||||
type CreateAuditLogParam struct {
|
||||
Data []AuditLog `json:"data"`
|
||||
}
|
||||
|
||||
// AuditQueryInput TODO
|
||||
// AuditQueryInput query audit logs input
|
||||
type AuditQueryInput struct {
|
||||
Condition AuditQueryCondition `json:"condition"`
|
||||
Page BasePage `json:"page,omitempty"`
|
||||
@@ -75,7 +75,7 @@ func (input *AuditQueryInput) Validate() errors.RawErrorInfo {
|
||||
return errors.RawErrorInfo{}
|
||||
}
|
||||
|
||||
// AuditQueryCondition TODO
|
||||
// AuditQueryCondition query audit logs condition struct
|
||||
type AuditQueryCondition struct {
|
||||
AuditType AuditType `json:"audit_type"`
|
||||
User string `json:"user"`
|
||||
@@ -107,7 +107,7 @@ func (a *AuditQueryCondition) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// OperationTimeCondition TODO
|
||||
// OperationTimeCondition operation time condition struct
|
||||
type OperationTimeCondition struct {
|
||||
Start string `json:"start"`
|
||||
End string `json:"end"`
|
||||
@@ -174,7 +174,7 @@ type InstAuditCondition struct {
|
||||
ID []int64 `json:"id"`
|
||||
}
|
||||
|
||||
// AuditLog TODO
|
||||
// AuditLog struct for audit log
|
||||
type AuditLog struct {
|
||||
ID int64 `json:"id" bson:"id"`
|
||||
// AuditType is a high level abstract of the resource managed by this cmdb.
|
||||
|
||||
@@ -40,7 +40,7 @@ type CloudAccount struct {
|
||||
LastTime time.Time `json:"last_time" bson:"last_time"`
|
||||
}
|
||||
|
||||
// Validate TODO
|
||||
// Validate account validate
|
||||
func (c *CloudAccount) Validate() (rawError errors.RawErrorInfo) {
|
||||
if c.AccountName == "" {
|
||||
return errors.RawErrorInfo{
|
||||
|
||||
@@ -139,6 +139,11 @@ func (c *DynamicGroupCondition) Validate(attributeMap map[string]string) error {
|
||||
return validAttributeValueType(attrType, c.Value)
|
||||
}
|
||||
|
||||
return c.validateByOperator(operator, attrType, attributeType)
|
||||
}
|
||||
|
||||
func (c *DynamicGroupCondition) validateByOperator(operator, attrType, attributeType string) error {
|
||||
|
||||
switch operator {
|
||||
case DynamicGroupOperatorEQ, DynamicGroupOperatorNE:
|
||||
return validAttributeValueType(attrType, c.Value)
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"configcenter/src/common/metadata"
|
||||
"configcenter/src/common/util"
|
||||
"configcenter/src/common/valid"
|
||||
"configcenter/src/common/valid/attribute/manager"
|
||||
)
|
||||
|
||||
// ValidPropertyOption valid property field option
|
||||
@@ -58,6 +59,14 @@ func ValidPropertyOption(kit *rest.Kit, propertyType string, option interface{},
|
||||
return ValidIDRuleOption(kit, option, attrTypeMap)
|
||||
}
|
||||
|
||||
if handle, ok := manager.Get(propertyType); ok {
|
||||
if err := handle.ValidateOption(kit.Ctx, option, extraOpt); err != nil {
|
||||
blog.Errorf("valid property option failed, property type: %s, option: %+v, extra opt: %+v, err: %v, rid: %s",
|
||||
propertyType, option, extraOpt, err, kit.Rid)
|
||||
return kit.CCError.Errorf(common.CCErrCommParamsIsInvalid, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
16
src/common/valid/attribute/init/README.md
Normal file
16
src/common/valid/attribute/init/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
### 说明
|
||||
|
||||
后续将插件的import放到目录下
|
||||
|
||||
##### 注意事项
|
||||
|
||||
- package 名字,必须为 init
|
||||
- 不允许写业务逻辑
|
||||
|
||||
```
|
||||
package init
|
||||
import (
|
||||
_ "configcenter/src/common/valid/attribute/plugins"
|
||||
)
|
||||
|
||||
```
|
||||
2
src/common/valid/attribute/init/init.go
Normal file
2
src/common/valid/attribute/init/init.go
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package init for plugin initialization
|
||||
package init
|
||||
11
src/common/valid/attribute/manager/attribute.go
Normal file
11
src/common/valid/attribute/manager/attribute.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Package manager provides a way to manage attributes in the system.
|
||||
package manager
|
||||
|
||||
import (
|
||||
"configcenter/src/common/valid/attribute/manager/register"
|
||||
// import init to register all attributes
|
||||
_ "configcenter/src/common/valid/attribute/init"
|
||||
)
|
||||
|
||||
// Get returns the Attribute by name
|
||||
var Get = register.Get
|
||||
68
src/common/valid/attribute/manager/register/attribute.go
Normal file
68
src/common/valid/attribute/manager/register/attribute.go
Normal file
@@ -0,0 +1,68 @@
|
||||
// Package register for attribute type registration and constraint
|
||||
package register
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"configcenter/src/common/blog"
|
||||
"configcenter/src/common/mapstr"
|
||||
)
|
||||
|
||||
var (
|
||||
attrTypeMap = map[string]AttributeTypeI{}
|
||||
)
|
||||
|
||||
// AttributeTypeI interface defines the methods for attribute types
|
||||
type AttributeTypeI interface {
|
||||
// Name 展示名字
|
||||
Name() string
|
||||
// DisplayName 类型的唯一名字
|
||||
DisplayName() string
|
||||
// RealType common.FieldTypeXXX , 必须是基础之一
|
||||
RealType() string
|
||||
|
||||
// Info 描述信息
|
||||
Info() string
|
||||
|
||||
// 预留暂未使用, 在 Validate 前处理, 做数据转换,
|
||||
// Transform(kit *rest.Kit,value interface{})(interface{}, error)
|
||||
|
||||
// Validate 实际校验方法
|
||||
Validate(ctx context.Context, objID string, propertyType string, required bool, option interface{}, value interface{}) error
|
||||
// FillLostValue 填充默认值
|
||||
FillLostValue(ctx context.Context, valData mapstr.MapStr, propertyId string, defaultValue, option interface{}) error
|
||||
// ValidateOption 校验 Option
|
||||
ValidateOption(ctx context.Context, option interface{}, defaultVal interface{}) error
|
||||
}
|
||||
|
||||
// Register attribute type
|
||||
// attrTypeMap is a map of attribute name to Attribute interface
|
||||
// this function is used to register a new attribute type
|
||||
// it will panic if the attribute name is empty or already exists
|
||||
// it is called in init() function of each attribute type file
|
||||
// so that all attribute types are registered when the program starts, not at runtime
|
||||
func Register(attr AttributeTypeI) {
|
||||
if attr == nil {
|
||||
blog.Errorf("register attribute is nil")
|
||||
panic("register attribute is nil")
|
||||
}
|
||||
|
||||
name := attr.Name()
|
||||
if name == "" {
|
||||
blog.Errorf("register attribute name is empty")
|
||||
panic("register attribute name is empty")
|
||||
}
|
||||
|
||||
if _, exists := attrTypeMap[name]; exists {
|
||||
blog.Errorf("attribute %s already exists", name)
|
||||
panic(fmt.Sprintf("attribute %s already exists", name))
|
||||
}
|
||||
attrTypeMap[name] = attr
|
||||
}
|
||||
|
||||
// Get returns the Attribute by name
|
||||
func Get(name string) (AttributeTypeI, bool) {
|
||||
a, ok := attrTypeMap[name]
|
||||
return a, ok
|
||||
}
|
||||
7
src/common/valid/attribute/plugins/README.md
Normal file
7
src/common/valid/attribute/plugins/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
### 说明
|
||||
|
||||
预留插件实现目录
|
||||
##### 注意事项
|
||||
|
||||
- package 名字,必须为 plugins
|
||||
|
||||
@@ -1169,17 +1169,17 @@ func (h *HostSnap) getHostByVal(header http.Header, rid string, cloudID int64, i
|
||||
}
|
||||
|
||||
func getIPsFromMsg(val *gjson.Result, agentID string, rid string) ([]string, []string) {
|
||||
ipv4Map := make(map[string]struct{})
|
||||
ipv6Map := make(map[string]struct{})
|
||||
ipv4List := make([]string, 0)
|
||||
ipv6List := make([]string, 0)
|
||||
|
||||
rootIP := val.Get("ip").String()
|
||||
rootIP = strings.TrimSpace(rootIP)
|
||||
if rootIP != metadata.IPv4LoopBackIpPrefix && rootIP != metadata.IPv6LoopBackIp &&
|
||||
!strings.HasPrefix(rootIP, metadata.IPv6LinkLocalAddressPrefix) && net.ParseIP(rootIP) != nil {
|
||||
if strings.Contains(rootIP, ":") {
|
||||
ipv6Map[rootIP] = struct{}{}
|
||||
ipv6List = append(ipv6List, rootIP)
|
||||
} else {
|
||||
ipv4Map[rootIP] = struct{}{}
|
||||
ipv4List = append(ipv4List, rootIP)
|
||||
}
|
||||
}
|
||||
// need to be compatible with the old and new versions of the format
|
||||
@@ -1234,22 +1234,18 @@ func getIPsFromMsg(val *gjson.Result, agentID string, rid string) ([]string, []s
|
||||
}
|
||||
|
||||
if strings.Contains(ip, ":") {
|
||||
ipv6Map[ip] = struct{}{}
|
||||
} else {
|
||||
ipv4Map[ip] = struct{}{}
|
||||
if !util.Contains(ipv6List, ip) {
|
||||
ipv6List = append(ipv6List, ip)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if !util.Contains(ipv4List, ip) {
|
||||
ipv4List = append(ipv4List, ip)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ipv4List := make([]string, 0)
|
||||
for ipv4 := range ipv4Map {
|
||||
ipv4List = append(ipv4List, ipv4)
|
||||
}
|
||||
|
||||
ipv6List := make([]string, 0)
|
||||
for ipv6 := range ipv6Map {
|
||||
ipv6List = append(ipv6List, ipv6)
|
||||
}
|
||||
return ipv4List, ipv6List
|
||||
}
|
||||
|
||||
|
||||
@@ -1028,6 +1028,10 @@ func (sh *searchHost) searchByHostConds() errors.CCError {
|
||||
}
|
||||
}
|
||||
|
||||
return sh.searchHost(condition)
|
||||
}
|
||||
|
||||
func (sh *searchHost) searchHost(condition map[string]interface{}) error {
|
||||
query := &metadata.QueryInput{
|
||||
Condition: condition,
|
||||
TimeCondition: sh.conds.hostCond.TimeCondition,
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"configcenter/src/ac/iam"
|
||||
"configcenter/src/common/auth"
|
||||
"configcenter/src/common/core/cc/config"
|
||||
auditconf "configcenter/src/source_controller/cacheservice/audit/config"
|
||||
"configcenter/src/storage/dal/mongo"
|
||||
"configcenter/src/storage/dal/redis"
|
||||
|
||||
@@ -38,6 +39,7 @@ type Config struct {
|
||||
WatchMongo mongo.Config
|
||||
Redis redis.Config
|
||||
Auth iam.AuthConfig
|
||||
Audit *auditconf.Config
|
||||
}
|
||||
|
||||
// NewServerOption create a ServerOption object
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"configcenter/src/common/blog"
|
||||
"configcenter/src/common/types"
|
||||
"configcenter/src/source_controller/cacheservice/app/options"
|
||||
auditconf "configcenter/src/source_controller/cacheservice/audit/config"
|
||||
cachesvr "configcenter/src/source_controller/cacheservice/service"
|
||||
"configcenter/src/storage/driver/mongodb"
|
||||
"configcenter/src/storage/driver/redis"
|
||||
@@ -63,8 +64,9 @@ func Run(ctx context.Context, cancel context.CancelFunc, op *options.ServerOptio
|
||||
input := &backbone.BackboneParameter{
|
||||
ConfigUpdate: cacheSvr.onCacheServiceConfigUpdate,
|
||||
ConfigPath: op.ServConf.ExConfig,
|
||||
SrvRegdiscv: backbone.SrvRegdiscv{Regdiscv: op.ServConf.RegDiscover, TLSConfig: op.ServConf.GetTLSClientConf()},
|
||||
SrvInfo: svrInfo,
|
||||
SrvRegdiscv: backbone.SrvRegdiscv{Regdiscv: op.ServConf.RegDiscover,
|
||||
TLSConfig: op.ServConf.GetTLSClientConf()},
|
||||
SrvInfo: svrInfo,
|
||||
}
|
||||
|
||||
engine, err := backbone.NewBackbone(ctx, input)
|
||||
@@ -144,5 +146,13 @@ func initResource(cacheSvr *CacheServer) error {
|
||||
return err
|
||||
}
|
||||
|
||||
cacheSvr.Config.Audit = new(auditconf.Config)
|
||||
if cc.IsExist("auditCenter") {
|
||||
if err = cc.UnmarshalKey("auditCenter", cacheSvr.Config.Audit); err != nil {
|
||||
blog.Errorf("parse audit center config failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
63
src/source_controller/cacheservice/audit/audit.go
Normal file
63
src/source_controller/cacheservice/audit/audit.go
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making
|
||||
* 蓝鲸智云 - 配置平台 (BlueKing - Configuration System) available.
|
||||
* Copyright (C) 2017 Tencent. All rights reserved.
|
||||
* Licensed under the MIT License (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at http://opensource.org/licenses/MIT
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
* We undertake not to change the open source license (MIT license) applicable
|
||||
* to the current version of the project delivered to anyone in the future.
|
||||
*/
|
||||
|
||||
// Package audit is the data reporting service for audit center
|
||||
package audit
|
||||
|
||||
import (
|
||||
"configcenter/src/common/blog"
|
||||
"configcenter/src/source_controller/cacheservice/audit/config"
|
||||
"configcenter/src/storage/stream"
|
||||
)
|
||||
|
||||
// Audit is the data reporting service for audit center
|
||||
type Audit struct {
|
||||
conf *config.Config
|
||||
loopW stream.LoopInterface
|
||||
client *auditClient
|
||||
}
|
||||
|
||||
// RunAuditDataReporting run audit data reporting
|
||||
func RunAuditDataReporting(conf *config.Config, loopW stream.LoopInterface) error {
|
||||
if conf == nil || !conf.Enabled {
|
||||
blog.Info("audit data reporting is disabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := conf.Validate(); err != nil {
|
||||
blog.Errorf("audit data reporting config(%+v) is invalid, err: %v", *conf, err)
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := newAuditClient(conf)
|
||||
if err != nil {
|
||||
blog.Errorf("init audit client failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
audit := &Audit{
|
||||
conf: conf,
|
||||
loopW: loopW,
|
||||
client: client,
|
||||
}
|
||||
|
||||
if err = audit.watchAudit(); err != nil {
|
||||
blog.Errorf("watch audit event and push to audit center failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
95
src/source_controller/cacheservice/audit/client.go
Normal file
95
src/source_controller/cacheservice/audit/client.go
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making
|
||||
* 蓝鲸智云 - 配置平台 (BlueKing - Configuration System) available.
|
||||
* Copyright (C) 2017 Tencent. All rights reserved.
|
||||
* Licensed under the MIT License (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at http://opensource.org/licenses/MIT
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
* We undertake not to change the open source license (MIT license) applicable
|
||||
* to the current version of the project delivered to anyone in the future.
|
||||
*/
|
||||
|
||||
package audit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"configcenter/src/apimachinery/rest"
|
||||
"configcenter/src/apimachinery/util"
|
||||
"configcenter/src/common/blog"
|
||||
"configcenter/src/source_controller/cacheservice/audit/config"
|
||||
|
||||
"go.opentelemetry.io/proto/otlp/collector/logs/v1"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
)
|
||||
|
||||
type auditClient struct {
|
||||
client rest.ClientInterface
|
||||
token string
|
||||
}
|
||||
|
||||
// ReportAuditData report audit data to audit center
|
||||
func (a *auditClient) ReportAuditData(ctx context.Context, h http.Header, opt *v1.ExportLogsServiceRequest) error {
|
||||
h.Set("Content-Type", "application/json")
|
||||
h.Set("X-BK-TOKEN", a.token)
|
||||
|
||||
body, err := protojson.Marshal(opt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal audit request failed, err: %v", err)
|
||||
}
|
||||
|
||||
res := a.client.Post().
|
||||
WithContext(ctx).
|
||||
Body(body).
|
||||
SubResourcef("/v1/logs").
|
||||
WithHeaders(h).
|
||||
Do()
|
||||
|
||||
if res.StatusCode >= 300 || res.Err != nil {
|
||||
return fmt.Errorf("http request failed, err: %v, status %s", res.Err, res.Status)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// newAuditClient new audit center client
|
||||
func newAuditClient(conf *config.Config) (*auditClient, error) {
|
||||
client, err := util.NewClient(nil)
|
||||
if err != nil {
|
||||
blog.Errorf("new http client failed, err: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &util.Capability{
|
||||
Client: client,
|
||||
Discover: &discovery{endpoint: conf.Endpoint},
|
||||
}
|
||||
|
||||
restCli := rest.NewRESTClient(c, "/")
|
||||
|
||||
return &auditClient{
|
||||
client: restCli,
|
||||
token: conf.Token,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type discovery struct {
|
||||
endpoint string
|
||||
}
|
||||
|
||||
// GetServers get servers
|
||||
func (s *discovery) GetServers() ([]string, error) {
|
||||
return []string{s.endpoint}, nil
|
||||
}
|
||||
|
||||
// GetServersChan get servers chan
|
||||
func (s *discovery) GetServersChan() chan []string {
|
||||
return nil
|
||||
}
|
||||
54
src/source_controller/cacheservice/audit/config/config.go
Normal file
54
src/source_controller/cacheservice/audit/config/config.go
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making
|
||||
* 蓝鲸智云 - 配置平台 (BlueKing - Configuration System) available.
|
||||
* Copyright (C) 2017 Tencent. All rights reserved.
|
||||
* Licensed under the MIT License (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at http://opensource.org/licenses/MIT
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
* We undertake not to change the open source license (MIT license) applicable
|
||||
* to the current version of the project delivered to anyone in the future.
|
||||
*/
|
||||
|
||||
// Package config is the audit data reporting configuration
|
||||
package config
|
||||
|
||||
import "errors"
|
||||
|
||||
// Config is the audit center related configuration
|
||||
type Config struct {
|
||||
Enabled bool `mapstructure:"enabled"`
|
||||
AppCode string `mapstructure:"appCode"`
|
||||
AppSecret string `mapstructure:"appSecret"`
|
||||
Endpoint string `mapstructure:"endpoint"`
|
||||
Token string `mapstructure:"token"`
|
||||
}
|
||||
|
||||
// Validate Config
|
||||
func (c Config) Validate() error {
|
||||
if !c.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.AppCode == "" {
|
||||
return errors.New("appCode is not set")
|
||||
}
|
||||
|
||||
if c.AppSecret == "" {
|
||||
return errors.New("appSecret is not set")
|
||||
}
|
||||
|
||||
if c.Endpoint == "" {
|
||||
return errors.New("endpoint is not set")
|
||||
}
|
||||
|
||||
if c.Token == "" {
|
||||
return errors.New("token is not set")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
255
src/source_controller/cacheservice/audit/watch.go
Normal file
255
src/source_controller/cacheservice/audit/watch.go
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making
|
||||
* 蓝鲸智云 - 配置平台 (BlueKing - Configuration System) available.
|
||||
* Copyright (C) 2017 Tencent. All rights reserved.
|
||||
* Licensed under the MIT License (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at http://opensource.org/licenses/MIT
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
* We undertake not to change the open source license (MIT license) applicable
|
||||
* to the current version of the project delivered to anyone in the future.
|
||||
*/
|
||||
|
||||
package audit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"configcenter/src/common"
|
||||
"configcenter/src/common/blog"
|
||||
"configcenter/src/common/mapstr"
|
||||
"configcenter/src/common/metadata"
|
||||
"configcenter/src/common/util"
|
||||
tokenhandler "configcenter/src/source_controller/cacheservice/cache/token-handler"
|
||||
"configcenter/src/storage/driver/mongodb"
|
||||
"configcenter/src/storage/stream/types"
|
||||
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
|
||||
collectorlogs "go.opentelemetry.io/proto/otlp/collector/logs/v1"
|
||||
commonv1 "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
logsv1 "go.opentelemetry.io/proto/otlp/logs/v1"
|
||||
resourcev1 "go.opentelemetry.io/proto/otlp/resource/v1"
|
||||
)
|
||||
|
||||
// watchAudit watch audit log event and push to audit center
|
||||
func (a *Audit) watchAudit() error {
|
||||
ctx := util.SetDBReadPreference(context.Background(), common.SecondaryPreferredMode)
|
||||
|
||||
name := "audit"
|
||||
tokenHandler := tokenhandler.NewSingleTokenHandler(name, mongodb.Client())
|
||||
|
||||
startAtTime, err := tokenHandler.GetStartWatchTime(ctx)
|
||||
if err != nil {
|
||||
blog.Errorf("get %s start watch time failed, err: %v", name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
operationType := types.Insert
|
||||
loopOptions := &types.LoopBatchOptions{
|
||||
LoopOptions: types.LoopOptions{
|
||||
Name: name,
|
||||
WatchOpt: &types.WatchOptions{
|
||||
Options: types.Options{
|
||||
OperationType: &operationType,
|
||||
Filter: make(mapstr.MapStr),
|
||||
EventStruct: new(metadata.AuditLog),
|
||||
Collection: common.BKTableNameAuditLog,
|
||||
StartAtTime: startAtTime,
|
||||
WatchFatalErrorCallback: tokenHandler.ResetWatchToken,
|
||||
},
|
||||
},
|
||||
TokenHandler: tokenHandler,
|
||||
RetryOptions: &types.RetryOptions{
|
||||
MaxRetryCount: 2,
|
||||
RetryDuration: 1 * time.Second,
|
||||
},
|
||||
},
|
||||
EventHandler: &types.BatchHandler{
|
||||
DoBatch: a.doBatch,
|
||||
},
|
||||
BatchSize: 100,
|
||||
}
|
||||
|
||||
if err = a.loopW.WithBatch(loopOptions); err != nil {
|
||||
blog.Errorf("watch %s failed, err: %v", name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// doBatch batch handle audit event
|
||||
func (a *Audit) doBatch(es []*types.Event) (retry bool) {
|
||||
if len(es) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
rid := es[0].ID()
|
||||
|
||||
logs := make([]*logsv1.ResourceLogs, 0)
|
||||
|
||||
for idx := range es {
|
||||
event := es[idx]
|
||||
|
||||
if event.OperationType != types.Insert {
|
||||
blog.V(4).Infof("received ignored audit event operation type: %s, doc: %s, rid: %s", event.OperationType,
|
||||
event.DocBytes, rid)
|
||||
continue
|
||||
}
|
||||
|
||||
resLog, isValid := a.convertToResourceLog(event, rid)
|
||||
if !isValid {
|
||||
blog.V(4).Infof("received invalid audit event: %+v, rid: %s", *event, rid)
|
||||
continue
|
||||
}
|
||||
logs = append(logs, resLog)
|
||||
|
||||
blog.V(5).Infof("watch audit, received oid: %s, op-time: %s event, rid: %s", event.Oid,
|
||||
event.ClusterTime.String(), rid)
|
||||
}
|
||||
|
||||
if len(logs) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
req := &collectorlogs.ExportLogsServiceRequest{
|
||||
ResourceLogs: logs,
|
||||
}
|
||||
err := a.client.ReportAuditData(context.Background(), make(http.Header), req)
|
||||
if err != nil {
|
||||
blog.ErrorJSON("report audit log data failed, err: %s, req: %s, rid: %s", err, req, rid)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *Audit) convertToResourceLog(event *types.Event, rid string) (*logsv1.ResourceLogs, bool) {
|
||||
auditLog, ok := event.Document.(*metadata.AuditLog)
|
||||
if !ok {
|
||||
blog.Errorf("received invalid audit event doc(%#v), oid: %s, rid: %s", event.Document, event.Oid, rid)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
detail, err := json.Marshal(auditLog.OperationDetail)
|
||||
if err != nil {
|
||||
blog.Errorf("marshal audit log detail failed, err: %v, auditLog: %v, rid: %s", err, *auditLog, rid)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
var instanceID string
|
||||
if intID, err := util.GetInt64ByInterface(auditLog.ResourceID); err == nil {
|
||||
instanceID = strconv.FormatInt(intID, 10)
|
||||
} else {
|
||||
instanceID = util.GetStrByInterface(auditLog.ResourceID)
|
||||
}
|
||||
|
||||
logAttributes := []*commonv1.KeyValue{
|
||||
a.convertToStrKeyValue("event_id", strconv.FormatInt(auditLog.ID, 10)),
|
||||
a.convertToStrKeyValue("event_content", string(detail)),
|
||||
a.convertToStrKeyValue("request_id", auditLog.RequestID),
|
||||
a.convertToIntKeyValue("start_time", auditLog.OperationTime.UnixMilli()),
|
||||
a.convertToIntKeyValue("end_time", auditLog.OperationTime.UnixMilli()),
|
||||
a.convertToStrKeyValue("bk_app_code", a.conf.AppCode),
|
||||
a.convertToStrKeyValue("action_id", string(auditLog.Action)),
|
||||
a.convertToStrKeyValue("resource_type_id", string(auditLog.ResourceType)),
|
||||
a.convertToStrKeyValue("instance_id", instanceID),
|
||||
a.convertToStrKeyValue("instance_name", auditLog.ResourceName),
|
||||
a.convertToIntKeyValue("instance_sensitivity", 0),
|
||||
a.convertToStrKeyValue("instance_data", "{}"),
|
||||
a.convertToStrKeyValue("instance_origin_data", "{}"),
|
||||
a.convertToStrKeyValue("extend_data", "{}"),
|
||||
a.convertToIntKeyValue("result_code", 0),
|
||||
a.convertToStrKeyValue("result_content", ""),
|
||||
a.convertToStrKeyValue("bk_log_scope", "bk_audit_event"),
|
||||
}
|
||||
|
||||
logAttributes = append(logAttributes, a.parseUserInfo(auditLog)...)
|
||||
logAttributes = append(logAttributes, a.parseScopeInfo(auditLog)...)
|
||||
|
||||
return &logsv1.ResourceLogs{
|
||||
Resource: &resourcev1.Resource{
|
||||
Attributes: []*commonv1.KeyValue{
|
||||
a.convertToStrKeyValue(string(semconv.ServiceNameKey), a.conf.AppCode),
|
||||
a.convertToStrKeyValue("bk.data.token", a.conf.Token),
|
||||
},
|
||||
},
|
||||
ScopeLogs: []*logsv1.ScopeLogs{{
|
||||
Scope: &commonv1.InstrumentationScope{
|
||||
Name: a.conf.AppCode,
|
||||
},
|
||||
LogRecords: []*logsv1.LogRecord{{
|
||||
TimeUnixNano: uint64(auditLog.OperationTime.UnixNano()),
|
||||
ObservedTimeUnixNano: uint64(time.Now().UnixNano()),
|
||||
SeverityNumber: logsv1.SeverityNumber_SEVERITY_NUMBER_INFO,
|
||||
SeverityText: "INFO",
|
||||
Attributes: logAttributes,
|
||||
}},
|
||||
}},
|
||||
SchemaUrl: semconv.SchemaURL,
|
||||
}, true
|
||||
}
|
||||
|
||||
func (a *Audit) parseUserInfo(auditLog *metadata.AuditLog) []*commonv1.KeyValue {
|
||||
var accessType, userIdentifyType int64
|
||||
if auditLog.OperateFrom == metadata.FromUser {
|
||||
accessType = 0
|
||||
userIdentifyType = 0
|
||||
} else {
|
||||
accessType = -1
|
||||
userIdentifyType = 1
|
||||
}
|
||||
|
||||
var userIdentifySrc, userIdentifySrcUsername string
|
||||
if auditLog.AppCode != "" {
|
||||
userIdentifySrc = "bk_app_code"
|
||||
userIdentifySrcUsername = auditLog.AppCode
|
||||
accessType = 1
|
||||
}
|
||||
|
||||
return []*commonv1.KeyValue{
|
||||
a.convertToStrKeyValue("username", auditLog.User),
|
||||
a.convertToStrKeyValue("user_identify_tenant_id", auditLog.SupplierAccount),
|
||||
a.convertToIntKeyValue("user_identify_type", userIdentifyType),
|
||||
a.convertToStrKeyValue("user_identify_src", userIdentifySrc),
|
||||
a.convertToStrKeyValue("user_identify_src_username", userIdentifySrcUsername),
|
||||
a.convertToIntKeyValue("access_type", accessType),
|
||||
a.convertToStrKeyValue("access_source_ip", ""),
|
||||
a.convertToStrKeyValue("access_user_agent", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Audit) parseScopeInfo(auditLog *metadata.AuditLog) []*commonv1.KeyValue {
|
||||
var scopeType, scopeID string
|
||||
if auditLog.BusinessID != 0 {
|
||||
scopeType = "biz"
|
||||
scopeID = strconv.FormatInt(auditLog.BusinessID, 10)
|
||||
}
|
||||
|
||||
return []*commonv1.KeyValue{
|
||||
a.convertToStrKeyValue("scope_type", scopeType),
|
||||
a.convertToStrKeyValue("scope_id", scopeID),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Audit) convertToStrKeyValue(key, value string) *commonv1.KeyValue {
|
||||
return &commonv1.KeyValue{
|
||||
Key: key,
|
||||
Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_StringValue{StringValue: value}},
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Audit) convertToIntKeyValue(key string, value int64) *commonv1.KeyValue {
|
||||
return &commonv1.KeyValue{
|
||||
Key: key,
|
||||
Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_IntValue{IntValue: value}},
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import (
|
||||
"configcenter/src/common/rdapi"
|
||||
"configcenter/src/common/webservice/restfulservice"
|
||||
"configcenter/src/source_controller/cacheservice/app/options"
|
||||
"configcenter/src/source_controller/cacheservice/audit"
|
||||
"configcenter/src/source_controller/cacheservice/cache"
|
||||
cacheop "configcenter/src/source_controller/cacheservice/cache"
|
||||
"configcenter/src/source_controller/cacheservice/event/bsrelation"
|
||||
@@ -69,6 +70,7 @@ type cacheService struct {
|
||||
core core.Core
|
||||
cacheSet *cache.ClientSet
|
||||
authManager *extensions.AuthManager
|
||||
audit *audit.Audit
|
||||
}
|
||||
|
||||
// SetConfig TODO
|
||||
@@ -78,11 +80,10 @@ func (s *cacheService) SetConfig(cfg options.Config, engine *backbone.Engine, er
|
||||
s.cfg = cfg
|
||||
s.engine = engine
|
||||
|
||||
if nil != err {
|
||||
if err != nil {
|
||||
s.err = err
|
||||
}
|
||||
|
||||
if nil != lang {
|
||||
if lang != nil {
|
||||
s.langFactory = make(map[common.LanguageType]language.DefaultCCLanguageIf)
|
||||
s.langFactory[common.Chinese] = lang.CreateDefaultCCLanguageIf(string(common.Chinese))
|
||||
s.langFactory[common.English] = lang.CreateDefaultCCLanguageIf(string(common.English))
|
||||
@@ -103,7 +104,6 @@ func (s *cacheService) SetConfig(cfg options.Config, engine *backbone.Engine, er
|
||||
blog.Errorf("new loop stream failed, err: %v", loopErr)
|
||||
return loopErr
|
||||
}
|
||||
|
||||
event, eventErr := reflector.NewReflector(s.cfg.Mongo.GetMongoConf())
|
||||
if eventErr != nil {
|
||||
blog.Errorf("new reflector failed, err: %v", eventErr)
|
||||
@@ -135,8 +135,7 @@ func (s *cacheService) SetConfig(cfg options.Config, engine *backbone.Engine, er
|
||||
return dbErr
|
||||
}
|
||||
|
||||
flowErr := flow.NewEvent(watcher, engine.ServiceManageInterface, watchDB, ccDB)
|
||||
if flowErr != nil {
|
||||
if flowErr := flow.NewEvent(watcher, engine.ServiceManageInterface, watchDB, ccDB); flowErr != nil {
|
||||
blog.Errorf("new watch event failed, err: %v", flowErr)
|
||||
return flowErr
|
||||
}
|
||||
@@ -151,6 +150,9 @@ func (s *cacheService) SetConfig(cfg options.Config, engine *backbone.Engine, er
|
||||
return err
|
||||
}
|
||||
|
||||
if err := audit.RunAuditDataReporting(cfg.Audit, loopW); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,9 @@ import (
|
||||
)
|
||||
|
||||
// CreateAccount TODO
|
||||
func (c *cloudOperation) CreateAccount(kit *rest.Kit, account *metadata.CloudAccount) (*metadata.CloudAccount, errors.CCErrorCoder) {
|
||||
func (c *cloudOperation) CreateAccount(kit *rest.Kit, account *metadata.CloudAccount) (
|
||||
*metadata.CloudAccount, errors.CCErrorCoder) {
|
||||
|
||||
if err := c.validCreateAccount(kit, account); nil != err {
|
||||
blog.Errorf("CreateAccount failed, valid error: %+v, rid: %s", err, kit.Rid)
|
||||
return nil, err
|
||||
@@ -50,7 +52,8 @@ func (c *cloudOperation) CreateAccount(kit *rest.Kit, account *metadata.CloudAcc
|
||||
|
||||
err = c.dbProxy.Table(common.BKTableNameCloudAccount).Insert(kit.Ctx, account)
|
||||
if err != nil {
|
||||
blog.ErrorJSON("CreateAccount failed, db insert failed, accountName: %s, err: %s, rid: %s", account.AccountName, err, kit.Rid)
|
||||
blog.ErrorJSON("CreateAccount failed, db insert failed, accountName: %s, err: %s, rid: %s", account.AccountName,
|
||||
err, kit.Rid)
|
||||
return nil, kit.CCError.CCError(common.CCErrCommDBInsertFailed)
|
||||
}
|
||||
// 不返回bk_secret_key的值
|
||||
@@ -59,7 +62,8 @@ func (c *cloudOperation) CreateAccount(kit *rest.Kit, account *metadata.CloudAcc
|
||||
}
|
||||
|
||||
// SearchAccount TODO
|
||||
func (c *cloudOperation) SearchAccount(kit *rest.Kit, option *metadata.SearchCloudOption) (*metadata.MultipleCloudAccount, errors.CCErrorCoder) {
|
||||
func (c *cloudOperation) SearchAccount(kit *rest.Kit,
|
||||
option *metadata.SearchCloudOption) (*metadata.MultipleCloudAccount, errors.CCErrorCoder) {
|
||||
accounts := []metadata.CloudAccount{}
|
||||
option.Condition = util.SetQueryOwner(option.Condition, kit.SupplierAccount)
|
||||
err := c.dbProxy.Table(common.BKTableNameCloudAccount).Find(option.Condition).Fields(option.Fields...).
|
||||
@@ -94,7 +98,8 @@ func (c *cloudOperation) SearchAccount(kit *rest.Kit, option *metadata.SearchClo
|
||||
if accountTaskcntMap[accounts[i].AccountID] == 0 {
|
||||
canDeleteAccount = true
|
||||
}
|
||||
results = append(results, metadata.CloudAccountWithExtraInfo{CloudAccount: accounts[i], CanDeleteAccount: canDeleteAccount})
|
||||
results = append(results,
|
||||
metadata.CloudAccountWithExtraInfo{CloudAccount: accounts[i], CanDeleteAccount: canDeleteAccount})
|
||||
}
|
||||
|
||||
return &metadata.MultipleCloudAccount{Count: int64(count), Info: results}, nil
|
||||
@@ -115,7 +120,8 @@ func (c *cloudOperation) UpdateAccount(kit *rest.Kit, accountID int64, option ma
|
||||
option.Remove(common.BKCloudIDField)
|
||||
option.Remove(common.BKOwnerIDField)
|
||||
if e := c.dbProxy.Table(common.BKTableNameCloudAccount).Update(kit.Ctx, filter, option); e != nil {
|
||||
blog.Errorf("UpdateAccount failed, mongodb failed, table: %s, filter: %+v, err: %+v, rid: %s", common.BKTableNameCloudAccount, filter, e, kit.Rid)
|
||||
blog.Errorf("UpdateAccount failed, mongodb failed, table: %s, filter: %+v, err: %+v, rid: %s",
|
||||
common.BKTableNameCloudAccount, filter, e, kit.Rid)
|
||||
return kit.CCError.CCError(common.CCErrCommDBUpdateFailed)
|
||||
}
|
||||
return nil
|
||||
@@ -131,18 +137,21 @@ func (c *cloudOperation) DeleteAccount(kit *rest.Kit, accountID int64) errors.CC
|
||||
filter := map[string]interface{}{common.BKCloudAccountID: accountID}
|
||||
filter = util.SetModOwner(filter, kit.SupplierAccount)
|
||||
if e := c.dbProxy.Table(common.BKTableNameCloudAccount).Delete(kit.Ctx, filter); e != nil {
|
||||
blog.Errorf("DeleteAccount failed, mongodb failed, table: %s, filter: %+v, err: %+v, rid: %s", common.BKTableNameCloudAccount, filter, e, kit.Rid)
|
||||
blog.Errorf("DeleteAccount failed, mongodb failed, table: %s, filter: %+v, err: %+v, rid: %s",
|
||||
common.BKTableNameCloudAccount, filter, e, kit.Rid)
|
||||
return kit.CCError.CCError(common.CCErrCommDBDeleteFailed)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SearchAccountConf 查询云厂商账户配置
|
||||
func (c *cloudOperation) SearchAccountConf(kit *rest.Kit, option *metadata.SearchCloudOption) (*metadata.MultipleCloudAccountConf, errors.CCErrorCoder) {
|
||||
func (c *cloudOperation) SearchAccountConf(kit *rest.Kit,
|
||||
option *metadata.SearchCloudOption) (*metadata.MultipleCloudAccountConf, errors.CCErrorCoder) {
|
||||
accountconfs := []metadata.CloudAccountConf{}
|
||||
option.Condition = util.SetQueryOwner(option.Condition, kit.SupplierAccount)
|
||||
err := c.dbProxy.Table(common.BKTableNameCloudAccount).Find(option.Condition).Fields(option.Fields...).
|
||||
Start(uint64(option.Page.Start)).Limit(uint64(option.Page.Limit)).Sort(option.Page.Sort).All(kit.Ctx, &accountconfs)
|
||||
Start(uint64(option.Page.Start)).Limit(uint64(option.Page.Limit)).Sort(option.Page.Sort).All(kit.Ctx,
|
||||
&accountconfs)
|
||||
if err != nil {
|
||||
blog.ErrorJSON("SearchAccount failed, db insert failed, option: %s, err: %s, rid: %s", option, err, kit.Rid)
|
||||
return nil, kit.CCError.CCError(common.CCErrCommDBSelectFailed)
|
||||
@@ -182,7 +191,8 @@ func (c *cloudOperation) getAcccountTaskcntMap(kit *rest.Kit) (map[int64]int64,
|
||||
Num int64 `bson:"num"`
|
||||
}
|
||||
resultAll := make([]accountTaskcnt, 0)
|
||||
if err := c.dbProxy.Table(common.BKTableNameCloudSyncTask).AggregateAll(kit.Ctx, aggregateCond, &resultAll); err != nil {
|
||||
if err := c.dbProxy.Table(common.BKTableNameCloudSyncTask).AggregateAll(kit.Ctx, aggregateCond,
|
||||
&resultAll); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -46,22 +46,18 @@ func New(cacheHost host.Interface) Searcher {
|
||||
}
|
||||
}
|
||||
|
||||
// ListHosts search host with topo node info and host property
|
||||
func (s *Searcher) ListHosts(kit *rest.Kit, option metadata.ListHosts) (*metadata.ListHostResult, error) {
|
||||
if key, err := option.Validate(); err != nil {
|
||||
blog.Errorf("list hosts option %+v is invalid, key: %s, err: %v, rid: %s", option, key, err, kit.Rid)
|
||||
return nil, err
|
||||
}
|
||||
func (s *Searcher) buildListHostsFilter(kit *rest.Kit, option metadata.ListHosts) ([]map[string]interface{},
|
||||
[]interface{}, bool, bool, error) {
|
||||
|
||||
needHostIDFilter, hostIDs, err := s.listHostIDsByRelation(kit, &option)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, false, false, err
|
||||
}
|
||||
|
||||
filters := make([]map[string]interface{}, 0)
|
||||
if needHostIDFilter {
|
||||
if len(hostIDs) == 0 {
|
||||
return &metadata.ListHostResult{Count: 0, Info: make([]map[string]interface{}, 0)}, nil
|
||||
return nil, nil, true, false, nil
|
||||
}
|
||||
filters = append(filters, map[string]interface{}{
|
||||
common.BKHostIDField: map[string]interface{}{common.BKDBIN: hostIDs},
|
||||
@@ -71,12 +67,30 @@ func (s *Searcher) ListHosts(kit *rest.Kit, option metadata.ListHosts) (*metadat
|
||||
propertyFilter, err := option.GetHostPropertyFilter(kit.Ctx)
|
||||
if err != nil {
|
||||
blog.Errorf("get host property filter failed, filter: %+v, err: %v, rid: %s", option, err, kit.Rid)
|
||||
return nil, err
|
||||
return nil, nil, false, false, err
|
||||
}
|
||||
if len(propertyFilter) > 0 {
|
||||
filters = append(filters, propertyFilter)
|
||||
}
|
||||
|
||||
return filters, hostIDs, false, needHostIDFilter, nil
|
||||
}
|
||||
|
||||
// ListHosts search host with topo node info and host property
|
||||
func (s *Searcher) ListHosts(kit *rest.Kit, option metadata.ListHosts) (*metadata.ListHostResult, error) {
|
||||
if key, err := option.Validate(); err != nil {
|
||||
blog.Errorf("list hosts option %+v is invalid, key: %s, err: %v, rid: %s", option, key, err, kit.Rid)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filters, hostIDs, noHostsMatched, needHostIDFilter, err := s.buildListHostsFilter(kit, option)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if noHostsMatched {
|
||||
return &metadata.ListHostResult{Count: 0, Info: make([]map[string]interface{}, 0)}, nil
|
||||
}
|
||||
|
||||
finalFilter := map[string]interface{}{}
|
||||
finalFilter = util.SetQueryOwner(finalFilter, util.ExtractOwnerFromContext(kit.Ctx))
|
||||
if len(filters) > 0 {
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"configcenter/src/common/metadata"
|
||||
"configcenter/src/common/util"
|
||||
"configcenter/src/common/valid"
|
||||
"configcenter/src/common/valid/attribute/manager"
|
||||
"configcenter/src/storage/driver/mongodb"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
@@ -40,63 +41,27 @@ func FillLostFieldValue(ctx context.Context, valData mapstr.MapStr, properties [
|
||||
if ok && (field.PropertyType != common.FieldTypeIDRule || val != "") {
|
||||
continue
|
||||
}
|
||||
|
||||
switch field.PropertyType {
|
||||
case common.FieldTypeSingleChar, common.FieldTypeLongChar:
|
||||
if err := fillLostStringFieldValue(valData, field); err != nil {
|
||||
return err
|
||||
}
|
||||
case common.FieldTypeEnum:
|
||||
if err := fillLostEnumFieldValue(ctx, valData, field); err != nil {
|
||||
return err
|
||||
}
|
||||
case common.FieldTypeEnumMulti:
|
||||
if err := fillLostEnumMultiFieldValue(ctx, valData, field); err != nil {
|
||||
return err
|
||||
}
|
||||
case common.FieldTypeEnumQuote:
|
||||
if err := fillLostEnumQuoteFieldValue(ctx, valData, field); err != nil {
|
||||
return err
|
||||
}
|
||||
case common.FieldTypeDate:
|
||||
if err := fillLostDateFieldValue(valData, field); err != nil {
|
||||
return err
|
||||
}
|
||||
case common.FieldTypeFloat:
|
||||
if err := fillLostFloatFieldValue(valData, field); err != nil {
|
||||
return err
|
||||
}
|
||||
case common.FieldTypeInt:
|
||||
if err := fillLostIntFieldValue(valData, field); err != nil {
|
||||
return err
|
||||
}
|
||||
case common.FieldTypeTime:
|
||||
if err := fillLostTimeFieldValue(valData, field); err != nil {
|
||||
return err
|
||||
}
|
||||
case common.FieldTypeUser:
|
||||
if err := fillLostUserFieldValue(valData, field); err != nil {
|
||||
return err
|
||||
}
|
||||
case common.FieldTypeOrganization:
|
||||
if err := fillLostOrganizationFieldValue(valData, field); err != nil {
|
||||
return err
|
||||
}
|
||||
case common.FieldTypeTimeZone:
|
||||
if err := fillLostTimeZoneFieldValue(valData, field); err != nil {
|
||||
return err
|
||||
}
|
||||
case common.FieldTypeList:
|
||||
if err := fillLostListFieldValue(valData, field); err != nil {
|
||||
return err
|
||||
}
|
||||
case common.FieldTypeBool:
|
||||
if err := fillLostBoolFieldValue(valData, field); err != nil {
|
||||
return err
|
||||
}
|
||||
case common.FieldTypeIDRule:
|
||||
if field.PropertyType == common.FieldTypeIDRule {
|
||||
idRuleField = &properties[idx]
|
||||
default:
|
||||
} else if fieldLostValueFunc, ok := ccSysFieldTypeRela[field.PropertyType]; ok {
|
||||
if err := fieldLostValueFunc(valData, field); err != nil {
|
||||
blog.Errorf("fill lost value failed, property type: %s, field: %+v, err: %v, rid: %s",
|
||||
field.PropertyType, field, err, util.ExtractRequestIDFromContext(ctx))
|
||||
return err
|
||||
}
|
||||
} else if fieldLostValueCtxFunc, ok := ccSysFieldTypeCtxRela[field.PropertyType]; ok {
|
||||
if err := fieldLostValueCtxFunc(ctx, valData, field); err != nil {
|
||||
blog.Errorf("fill lost value failed, property type: %s, field: %+v, err: %v, rid: %s",
|
||||
field.PropertyType, field, err, util.ExtractRequestIDFromContext(ctx))
|
||||
return err
|
||||
}
|
||||
} else if handle, ok := manager.Get(field.PropertyType); ok {
|
||||
if err := handle.FillLostValue(ctx, valData, field.PropertyID, field.Default, field.Option); err != nil {
|
||||
blog.Errorf("fill lost value failed, property type: %s, field: %+v, err: %v, rid: %s",
|
||||
field.PropertyType, field, err, util.ExtractRequestIDFromContext(ctx))
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
valData[field.PropertyID] = nil
|
||||
}
|
||||
}
|
||||
@@ -111,6 +76,27 @@ func FillLostFieldValue(ctx context.Context, valData mapstr.MapStr, properties [
|
||||
return nil
|
||||
}
|
||||
|
||||
var ccSysFieldTypeRela = map[string]func(valData mapstr.MapStr, field metadata.Attribute) error{
|
||||
common.FieldTypeSingleChar: fillLostStringFieldValue,
|
||||
common.FieldTypeLongChar: fillLostStringFieldValue,
|
||||
common.FieldTypeDate: fillLostDateFieldValue,
|
||||
common.FieldTypeFloat: fillLostFloatFieldValue,
|
||||
common.FieldTypeInt: fillLostIntFieldValue,
|
||||
common.FieldTypeTime: fillLostTimeFieldValue,
|
||||
common.FieldTypeUser: fillLostUserFieldValue,
|
||||
common.FieldTypeOrganization: fillLostOrganizationFieldValue,
|
||||
common.FieldTypeTimeZone: fillLostTimeZoneFieldValue,
|
||||
common.FieldTypeList: fillLostListFieldValue,
|
||||
common.FieldTypeBool: fillLostBoolFieldValue,
|
||||
}
|
||||
|
||||
var ccSysFieldTypeCtxRela = map[string]func(ctx context.Context, valData mapstr.MapStr, field metadata.Attribute) error{
|
||||
|
||||
common.FieldTypeEnum: fillLostEnumFieldValue,
|
||||
common.FieldTypeEnumMulti: fillLostEnumMultiFieldValue,
|
||||
common.FieldTypeEnumQuote: fillLostEnumQuoteFieldValue,
|
||||
}
|
||||
|
||||
func fillLostStringFieldValue(valData mapstr.MapStr, field metadata.Attribute) error {
|
||||
valData[field.PropertyID] = ""
|
||||
if field.Default == nil {
|
||||
|
||||
@@ -37,6 +37,7 @@ import (
|
||||
"configcenter/src/common/util"
|
||||
"configcenter/src/common/valid"
|
||||
attrvalid "configcenter/src/common/valid/attribute"
|
||||
"configcenter/src/common/valid/attribute/manager"
|
||||
"configcenter/src/storage/dal/types"
|
||||
"configcenter/src/storage/driver/mongodb"
|
||||
|
||||
@@ -506,6 +507,7 @@ var validAttrPropertyTypes = map[string]struct{}{
|
||||
|
||||
func (m *modelAttribute) checkAttributeValidity(kit *rest.Kit, attribute metadata.Attribute,
|
||||
propertyType string) error {
|
||||
|
||||
language := httpheader.GetLanguage(kit.Header)
|
||||
lang := m.language.CreateDefaultCCLanguageIf(language)
|
||||
if attribute.PropertyID != "" {
|
||||
@@ -542,9 +544,27 @@ func (m *modelAttribute) checkAttributeValidity(kit *rest.Kit, attribute metadat
|
||||
}
|
||||
}
|
||||
|
||||
if err := m.validPropertyType(kit, attribute, propertyType); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opt, ok := attribute.Option.(string); ok && opt != "" {
|
||||
if common.AttributeOptionMaxLength < utf8.RuneCountInString(opt) {
|
||||
return kit.CCError.Errorf(common.CCErrCommValExceedMaxFailed, lang.Language("model_attr_option_regex"),
|
||||
common.AttributeOptionMaxLength)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *modelAttribute) validPropertyType(kit *rest.Kit, attribute metadata.Attribute, propertyType string) error {
|
||||
|
||||
if attribute.PropertyType != "" {
|
||||
if _, exists := validAttrPropertyTypes[attribute.PropertyType]; !exists {
|
||||
return kit.CCError.Errorf(common.CCErrCommParamsIsInvalid, metadata.AttributeFieldPropertyType)
|
||||
if _, ok := manager.Get(attribute.PropertyType); !ok {
|
||||
return kit.CCError.Errorf(common.CCErrCommParamsIsInvalid, metadata.AttributeFieldPropertyType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -556,13 +576,6 @@ func (m *modelAttribute) checkAttributeValidity(kit *rest.Kit, attribute metadat
|
||||
}
|
||||
}
|
||||
|
||||
if opt, ok := attribute.Option.(string); ok && opt != "" {
|
||||
if common.AttributeOptionMaxLength < utf8.RuneCountInString(opt) {
|
||||
return kit.CCError.Errorf(common.CCErrCommValExceedMaxFailed, lang.Language("model_attr_option_regex"),
|
||||
common.AttributeOptionMaxLength)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
3
src/ui/.gitignore
vendored
3
src/ui/.gitignore
vendored
@@ -20,6 +20,9 @@ eslint.html
|
||||
# dev config
|
||||
/builder/config/index.dev*.js
|
||||
|
||||
# serve config
|
||||
/builder/config/index.svr.js
|
||||
|
||||
# mock
|
||||
/mock/*
|
||||
!/mock/index.js
|
||||
|
||||
67
src/ui/builder/serve.js
Normal file
67
src/ui/builder/serve.js
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Tencent is pleased to support the open source community by making 蓝鲸 available.
|
||||
* Copyright (C) 2017 Tencent. All rights reserved.
|
||||
* Licensed under the MIT License (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under
|
||||
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const express = require('express')
|
||||
const { createProxyMiddleware } = require('http-proxy-middleware')
|
||||
|
||||
// ---------- 运行时配置 ----------
|
||||
const svrConfig = require('./config/index.svr')
|
||||
const buildConfig = require('./config')
|
||||
|
||||
// ---------- 代理目标 ----------
|
||||
const PROXY_TARGET = svrConfig.apiTarget
|
||||
|
||||
// ---------- Express 实例 ----------
|
||||
const app = express()
|
||||
|
||||
// ---------- 1. 静态资源 ----------
|
||||
const staticDir = buildConfig.build.assetsRoot
|
||||
const publicPath = '/static'
|
||||
// 把本地 dist 目录挂到 /static
|
||||
app.use(publicPath, express.static(staticDir))
|
||||
|
||||
// ---------- 2. 读取并缓存 index.html 模板 ----------
|
||||
const indexPath = path.join(staticDir, 'index.html')
|
||||
if (!fs.existsSync(indexPath)) {
|
||||
console.error(`❌ 未找到模板文件 ${indexPath}`)
|
||||
process.exit(1)
|
||||
}
|
||||
const template = fs.readFileSync(indexPath, 'utf8')
|
||||
|
||||
// ---------- 3. 模板渲染 ----------
|
||||
function render(html, data) {
|
||||
return html.replace(/\{\{\.(\w+)\}\}/g, (_, key) => data[key] ?? '')
|
||||
}
|
||||
|
||||
// ---------- 4. 根路由 & 模板注入 ----------
|
||||
app.get('/', (req, res) => {
|
||||
res.set('Content-Type', 'text/html')
|
||||
res.send(render(template, svrConfig))
|
||||
})
|
||||
|
||||
// ---------- 5. 其余全部代理 ----------
|
||||
// 放在最末尾兜底,确保 /static 和 index.html 已处理完
|
||||
app.use(createProxyMiddleware({
|
||||
target: PROXY_TARGET,
|
||||
changeOrigin: true,
|
||||
logLevel: 'debug',
|
||||
}))
|
||||
|
||||
// ---------- 6. 启动 ----------
|
||||
const PORT = process.env.PORT || 3000
|
||||
app.listen(PORT, () => {
|
||||
console.log(`✅ SPA server listening on http://localhost:${PORT}`)
|
||||
console.log(` Static files -> http://localhost:${PORT}/static/**`)
|
||||
console.log(` Everything else -> ${PROXY_TARGET}`)
|
||||
})
|
||||
81
src/ui/builder/utils/build-hash-plugin.js
Normal file
81
src/ui/builder/utils/build-hash-plugin.js
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making 蓝鲸 available.
|
||||
* Copyright (C) 2017 Tencent. All rights reserved.
|
||||
* Licensed under the MIT License (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under
|
||||
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const crypto = require('crypto')
|
||||
|
||||
class BuildHashPlugin {
|
||||
constructor(options = {}) {
|
||||
this.options = Object.assign(
|
||||
{
|
||||
outputFile: 'build-hash.txt', // 默认输出文件名
|
||||
algorithm: 'sha256', // 哈希算法
|
||||
includeTimestamp: false, // 是否包含时间戳
|
||||
customContent: null, // 自定义内容,会添加到哈希中
|
||||
},
|
||||
options,
|
||||
)
|
||||
}
|
||||
|
||||
apply(compiler) {
|
||||
// 在完成构建后执行
|
||||
compiler.hooks.done.tap('BuildHashPlugin', (stats) => {
|
||||
const { compilation } = stats
|
||||
const outputPath = compilation.outputOptions.path || compiler.outputPath
|
||||
const hashFile = path.resolve(outputPath, this.options.outputFile)
|
||||
|
||||
// 1. 创建哈希生成器
|
||||
const hash = crypto.createHash(this.options.algorithm)
|
||||
|
||||
// 2. 添加构建时间戳(可选)
|
||||
if (this.options.includeTimestamp) {
|
||||
hash.update(Date.now().toString())
|
||||
}
|
||||
|
||||
// 3. 添加自定义内容(可选)
|
||||
if (typeof this.options.customContent === 'string') {
|
||||
hash.update(this.options.customContent)
|
||||
}
|
||||
|
||||
// 4. 添加编译信息
|
||||
hash.update(compilation.hash)
|
||||
|
||||
// 5. 添加所有依赖模块的修改时间
|
||||
compilation.fileDependencies.forEach((filePath) => {
|
||||
if (fs.existsSync(filePath)) {
|
||||
const stats = fs.statSync(filePath)
|
||||
hash.update(stats.mtimeMs.toString())
|
||||
}
|
||||
})
|
||||
|
||||
// 6. 生成最终哈希
|
||||
const finalHash = hash.digest('hex')
|
||||
|
||||
// 7. 确保输出目录存在
|
||||
if (!fs.existsSync(outputPath)) {
|
||||
fs.mkdirSync(outputPath, { recursive: true })
|
||||
}
|
||||
|
||||
// 8. 写入文件
|
||||
fs.writeFileSync(hashFile, finalHash)
|
||||
|
||||
// 9. 在控制台显示信息(可选)
|
||||
if (this.options.verbose !== false) {
|
||||
console.log(`\nBuild hash generated: ${finalHash}`)
|
||||
console.log(`Hash file written to: ${hashFile}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BuildHashPlugin
|
||||
@@ -18,10 +18,10 @@ const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const CopyPlugin = require('copy-webpack-plugin')
|
||||
const ProgressBarPlugin = require('progress-bar-webpack-plugin')
|
||||
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
|
||||
const chalk = require('chalk')
|
||||
const ReplaceStaticUrlPlugin = require('../utils/replace-static-url-plugin')
|
||||
const GrabAPIPlugin = require('../utils/grab-api-plugin')
|
||||
const chalk = require('chalk')
|
||||
|
||||
const BuildHashPlugin = require('../utils/build-hash-plugin')
|
||||
|
||||
const { isProd, resolveBase, modeValue } = require('../utils')
|
||||
const devEnv = require('../config/dev.env')
|
||||
@@ -95,7 +95,9 @@ const getProdPlugins = config => ([
|
||||
ignoreOrder: true
|
||||
}),
|
||||
|
||||
new ReplaceStaticUrlPlugin(config.build)
|
||||
new ReplaceStaticUrlPlugin(config.build),
|
||||
|
||||
new BuildHashPlugin()
|
||||
].concat((process.env.ANALYZER || config.build.bundleAnalyzerReport) ? [
|
||||
new BundleAnalyzerPlugin()
|
||||
] : []))
|
||||
|
||||
@@ -11,12 +11,13 @@
|
||||
"build": "node builder/build.js",
|
||||
"build:analyzer": "cross-env ANALYZER=true node builder/build.js",
|
||||
"start": "npm run dev -- --env",
|
||||
"serve": "node builder/serve.js",
|
||||
"lint": "eslint --ext .js,.vue ./src --cache",
|
||||
"lint-output": "eslint --ext .js,.vue --output-file ./eslint.html --format html ./src",
|
||||
"lint-fix": "eslint --fix --ext .js,.vue ./src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.1",
|
||||
"@babel/runtime": "^7.28.4",
|
||||
"@blueking/bkui-library": "^0.0.0-beta.6",
|
||||
"@blueking/functional-dependency": "^0.0.1-beta.10",
|
||||
"@blueking/login-modal": "^1.0.0",
|
||||
@@ -113,7 +114,7 @@
|
||||
"minimist": "^1.2.5",
|
||||
"os-browserify": "^0.3.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"path-to-regexp": "^6.2.1",
|
||||
"path-to-regexp": "^6.3.0",
|
||||
"postcss": "^8.2.10",
|
||||
"postcss-deep-scopable": "0.0.2",
|
||||
"postcss-loader": "^5.2.0",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Vue, { toRef, watch } from 'vue'
|
||||
import Vue, { computed, toRef, watch } from 'vue'
|
||||
import contentComponent from './export'
|
||||
import store from '@/store'
|
||||
import i18n from '@/i18n'
|
||||
@@ -21,6 +21,7 @@ import isEqual from 'lodash/isEqual'
|
||||
|
||||
let instance = null
|
||||
const [state, { setState, resetState }] = useState()
|
||||
const [{ all }] = useTask()
|
||||
const [, { reset: resetTask }] = useTask()
|
||||
const visible = toRef(state, 'visible')
|
||||
const title = toRef(state, 'title')
|
||||
@@ -46,9 +47,10 @@ watch(visible, (value) => {
|
||||
}, 200)
|
||||
})
|
||||
|
||||
const hasChange = () => !isEqual(state.originFields.value, fields.value) || step.value !== 1 || status.value === 'pending'
|
||||
const hasChange = computed(() => !isEqual(state.originFields.value, fields.value) || step.value !== 1 || status.value === 'pending')
|
||||
const allFinished = computed(() => all.value.length && all.value.every(task => task.state === 'finished'))
|
||||
const beforeClose = () => {
|
||||
if (!hasChange()) {
|
||||
if (!hasChange.value || allFinished.value) {
|
||||
return true
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
|
||||
@@ -55,7 +55,7 @@ const start = () => {
|
||||
const [state] = useState()
|
||||
const queue = new Array(Math.ceil(state.count.value / state.limit.value)).fill(null)
|
||||
.map((_, index) => ({
|
||||
name: `${state.bk_obj_id.value}_download_${index + 1}`,
|
||||
name: `${state.bk_obj_id.value}_download_${Date.now()}_${index + 1}`,
|
||||
state: 'waiting',
|
||||
page: {
|
||||
start: index * state.limit.value,
|
||||
|
||||
@@ -110,6 +110,7 @@
|
||||
:is="getComponentType(property)"
|
||||
:placeholder="getPlaceholder(property)"
|
||||
:property="property"
|
||||
:is-paste-split="getPasteSplit(property.bk_property_id)"
|
||||
:ref="`component-${property.id}`"
|
||||
v-bind="getBindProps(property)"
|
||||
v-model.trim="condition[property.id].value"
|
||||
@@ -225,7 +226,7 @@
|
||||
import Utils from './utils'
|
||||
import { isContainerObject } from '@/service/container/common'
|
||||
import ConditionPicker from '@/components/condition-picker'
|
||||
import { setCursorPosition, getConditionSelect, updatePropertySelect } from '@/utils/util'
|
||||
import { setCursorPosition, getConditionSelect, updatePropertySelect, isPasteSplit } from '@/utils/util'
|
||||
import useSideslider from '@/hooks/use-sideslider'
|
||||
import isEqual from 'lodash/isEqual'
|
||||
import EditableBlock from '@/components/editable-block/index.vue'
|
||||
@@ -402,6 +403,9 @@
|
||||
this.setChanged = setChanged
|
||||
},
|
||||
methods: {
|
||||
getPasteSplit(id) {
|
||||
return isPasteSplit(id)
|
||||
},
|
||||
hasAddSelected(val, oldVal, addSelect) {
|
||||
return val[0] && oldVal[0] && addSelect.length > 0
|
||||
},
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<component class="form-el"
|
||||
:is="getComponentType()"
|
||||
:placeholder="getPlaceholder()"
|
||||
:is-paste-split="getPasteSplit(property.bk_property_id)"
|
||||
v-bind="getBindProps()"
|
||||
v-model.trim="value"
|
||||
@active-change="handleActiveChange">
|
||||
@@ -53,6 +54,7 @@
|
||||
import { mapGetters } from 'vuex'
|
||||
import { isContainerObject } from '@/service/container/common'
|
||||
import { QUERY_OPERATOR, QUERY_OPERATOR_HOST_SYMBOL, QUERY_OPERATOR_HOST_DESC } from '@/utils/query-builder-operator'
|
||||
import { isPasteSplit } from '@/utils/util'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -110,6 +112,9 @@
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPasteSplit(id) {
|
||||
return isPasteSplit(id)
|
||||
},
|
||||
getPlaceholder() {
|
||||
return Utils.getPlaceholder(this.property)
|
||||
},
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
:is="getComponentType(property)"
|
||||
:placeholder="getPlaceholder(property)"
|
||||
:property="property"
|
||||
:is-paste-split="getPasteSplit(property.bk_property_id)"
|
||||
:ref="`component-${property.id}`"
|
||||
v-bind="getBindProps(property)"
|
||||
v-model.trim="condition[property.id].value"
|
||||
@@ -105,7 +106,7 @@
|
||||
import { setSearchQueryByCondition, resetConditionValue } from './general-model-filter.js'
|
||||
import Utils from './utils'
|
||||
import ConditionPicker from '@/components/condition-picker'
|
||||
import { getConditionSelect, updatePropertySelect } from '@/utils/util'
|
||||
import { getConditionSelect, updatePropertySelect, isPasteSplit } from '@/utils/util'
|
||||
import isEqual from 'lodash/isEqual'
|
||||
import useSideslider from '@/hooks/use-sideslider'
|
||||
import { QUERY_OPERATOR, QUERY_OPERATOR_OTHER_SYMBOL, QUERY_OPERATOR_OTHER_DESC } from '@/utils/query-builder-operator'
|
||||
@@ -196,6 +197,9 @@
|
||||
this.setChanged = setChanged
|
||||
},
|
||||
methods: {
|
||||
getPasteSplit(id) {
|
||||
return isPasteSplit(id)
|
||||
},
|
||||
setCondition(nowCondition) {
|
||||
const newCondition = this.$tools.clone(this.filterCondition)
|
||||
Object.keys(nowCondition).forEach((id) => {
|
||||
|
||||
@@ -201,10 +201,14 @@
|
||||
}
|
||||
},
|
||||
defaultValue(val, old) {
|
||||
// 检测选中值变化,需要修正is_default
|
||||
// 检测选中值变化,需要修正is_default,这里值的类型都是string
|
||||
if (val && !isEqual(val, old)) {
|
||||
this.enumList.forEach((item) => {
|
||||
item.is_default = val.includes(item.id)
|
||||
if (this.isDefaultCompMultiple && Array.isArray(val)) {
|
||||
item.is_default = val.includes(item.id)
|
||||
} else {
|
||||
item.is_default = val === item.id
|
||||
}
|
||||
})
|
||||
this.$emit('input', this.enumList)
|
||||
}
|
||||
|
||||
@@ -151,8 +151,9 @@
|
||||
:value="defaultValue"
|
||||
:property="field">
|
||||
</cmdb-property-value>
|
||||
<!-- bool类型字段未设置与默认值false都将显示为false -->
|
||||
<span v-else
|
||||
class="property-value">{{defaultValue || '--'}}</span>
|
||||
class="property-value">{{defaultValue ?? '--'}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<template slot="footer" slot-scope="{ sticky }" v-if="canEdit">
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
|
||||
<script>
|
||||
import formMixins from '@/mixins/form'
|
||||
import { filterXSS } from '@/utils/util'
|
||||
import TableDefaultSettings from './table-default-settings.vue'
|
||||
import { PROPERTY_TYPES } from '@/dictionary/property-constants'
|
||||
|
||||
@@ -149,11 +150,7 @@
|
||||
return !property.editable || property.isreadonly
|
||||
},
|
||||
htmlEncode(placeholder) {
|
||||
let temp = document.createElement('div')
|
||||
temp.innerHTML = placeholder
|
||||
const output = temp.innerText
|
||||
temp = null
|
||||
return output
|
||||
return filterXSS(placeholder)
|
||||
},
|
||||
getPlaceholder(property) {
|
||||
const placeholderTxt = ['enum', 'list'].includes(property.bk_property_type) ? '请选择xx' : '请输入xx'
|
||||
|
||||
@@ -80,6 +80,7 @@
|
||||
.filter(value => value.length)
|
||||
}
|
||||
this.localValue = [...new Set([...this.localValue, ...val])]
|
||||
return this.localValue
|
||||
},
|
||||
handleInputChange(value) {
|
||||
this.$emit('inputchange', value)
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
.filter(value => value.length)
|
||||
}
|
||||
this.localValue = [...new Set([...this.localValue, ...val])]
|
||||
return this.localValue
|
||||
},
|
||||
handleInputChange(value) {
|
||||
this.$emit('inputchange', value)
|
||||
|
||||
@@ -95,15 +95,18 @@
|
||||
methods: {
|
||||
handlePasteFn(value) {
|
||||
if (!value) return
|
||||
if (this.onlyNumber && !isNumeric(value)) {
|
||||
return
|
||||
if (this.onlyNumber) {
|
||||
// 如果当前需要分割功能,在只能输入number的时候,需要将value去掉分隔符
|
||||
const detectionValue = this.isPasteSplit ? value.replace(/,|;|\n|\s/g, '') : value
|
||||
if (!isNumeric(detectionValue)) return
|
||||
}
|
||||
let val = [value]
|
||||
if (this.isPasteSplit && this.multiple) {
|
||||
val = (value.split(/,|;|\n/)).map(value => value.trim())
|
||||
val = (value.split(/,|;|\n|\s/)).map(value => value.trim())
|
||||
.filter(value => value.length)
|
||||
}
|
||||
this.localValue = [...new Set([...this.localValue, ...val])]
|
||||
return this.localValue
|
||||
},
|
||||
handleInputChange(value) {
|
||||
this.$emit('inputchange', value)
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
v-if="property.placeholder && $tools.isIconTipProperty(property.bk_property_type)"
|
||||
v-bk-tooltips="{
|
||||
trigger: 'mouseenter',
|
||||
content: property.placeholder
|
||||
content: htmlEncode(property.placeholder)
|
||||
}">
|
||||
</i>
|
||||
</bk-checkbox>
|
||||
@@ -65,7 +65,7 @@
|
||||
disabled: !property.placeholder,
|
||||
theme: 'light',
|
||||
trigger: 'click',
|
||||
content: property.placeholder
|
||||
content: htmlEncode(property.placeholder)
|
||||
}"
|
||||
v-bind="$tools.getValidateEvents(property)"
|
||||
v-validate="getValidateRules(property)"
|
||||
@@ -125,6 +125,7 @@
|
||||
import { BUILTIN_UNEDITABLE_FIELDS } from '@/dictionary/model-constants'
|
||||
import useSideslider from '@/hooks/use-sideslider'
|
||||
import cmdbDefaultPicker from '@/components/ui/other/default-value-picker'
|
||||
import { filterXSS } from '@/utils/util'
|
||||
|
||||
export default {
|
||||
name: 'cmdb-form-multiple',
|
||||
@@ -242,11 +243,7 @@
|
||||
this.editable = editable
|
||||
},
|
||||
htmlEncode(placeholder) {
|
||||
let temp = document.createElement('div')
|
||||
temp.innerHTML = placeholder
|
||||
const output = temp.innerText
|
||||
temp = null
|
||||
return output
|
||||
return filterXSS(placeholder)
|
||||
},
|
||||
getProperty(id) {
|
||||
return this.properties.find(property => property.bk_property_id === id)
|
||||
|
||||
@@ -123,6 +123,7 @@
|
||||
import useSideslider from '@/hooks/use-sideslider'
|
||||
import isEqual from 'lodash/isEqual'
|
||||
import cmdbDefaultPicker from '@/components/ui/other/default-value-picker'
|
||||
import { filterXSS } from '@/utils/util'
|
||||
|
||||
export default {
|
||||
name: 'cmdb-form',
|
||||
@@ -258,11 +259,7 @@
|
||||
return property.isrequired
|
||||
},
|
||||
htmlEncode(placeholder) {
|
||||
let temp = document.createElement('div')
|
||||
temp.innerHTML = placeholder
|
||||
const output = temp.innerText
|
||||
temp = null
|
||||
return output
|
||||
return filterXSS(placeholder)
|
||||
},
|
||||
getValidateRules(property) {
|
||||
const rules = this.$tools.getValidateRules(property)
|
||||
|
||||
@@ -69,12 +69,7 @@
|
||||
props() {
|
||||
const props = { ...this.$attrs }
|
||||
if (this.api) {
|
||||
try {
|
||||
const url = new URL(this.api)
|
||||
props.api = `${window.API_HOST}proxy/get/usermanage${url.pathname}`
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
props.api = this.api
|
||||
} else {
|
||||
props.fuzzySearchMethod = this.fuzzySearchMethod
|
||||
props.exactSearchMethod = this.exactSearchMethod
|
||||
|
||||
@@ -33,21 +33,11 @@
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
return {
|
||||
api: window.ESB.userManage
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
api() {
|
||||
const { userManage } = window.ESB
|
||||
if (userManage) {
|
||||
try {
|
||||
const url = new URL(userManage)
|
||||
return `${window.API_HOST}proxy/get/usermanage${url.pathname}`
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
return ''
|
||||
},
|
||||
localValue: {
|
||||
get() {
|
||||
if (this.value) {
|
||||
|
||||
@@ -21,6 +21,9 @@ import {
|
||||
MENU_RESOURCE_PROJECT_COLLECTION
|
||||
} from '@/dictionary/menu-symbol'
|
||||
|
||||
// 字段搜索时,不受cmdb-search-xx组件isPastesplit属性切割影响的字段 (一定会切割)
|
||||
export const BUILTIN_PASTE_SPLIT_FIELDS = ['bk_biz_set_id', 'bk_biz_id', 'bk_host_id', 'bk_module_id', 'bk_set_id', 'id', 'bk_inst_id', 'bk_agent_id', 'bk_cloud_inst_id']
|
||||
|
||||
// 不能更新修改的字段(在可能发生编辑操作的页面里不显示出来)
|
||||
export const BUILTIN_UNEDITABLE_FIELDS = ['bk_updated_by', 'bk_updated_at', 'bk_created_by', 'bk_created_at']
|
||||
|
||||
|
||||
85
src/ui/src/hooks/use-timeout-poll.js
Normal file
85
src/ui/src/hooks/use-timeout-poll.js
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making 蓝鲸 available.
|
||||
* Copyright (C) 2017 Tencent. All rights reserved.
|
||||
* Licensed under the MIT License (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under
|
||||
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { getCurrentScope, onScopeDispose, ref } from 'vue'
|
||||
|
||||
export default function useTimeoutPoll(fn, interval, options) {
|
||||
const { immediate = false, max = 100 } = options || {}
|
||||
|
||||
const isActive = ref(false)
|
||||
|
||||
let timer = null
|
||||
|
||||
let times = 0
|
||||
|
||||
function clear() {
|
||||
if (timer) {
|
||||
clearTimeout(timer)
|
||||
timer = null
|
||||
}
|
||||
}
|
||||
|
||||
function start() {
|
||||
clear()
|
||||
timer = setTimeout(() => {
|
||||
timer = null
|
||||
|
||||
loop()
|
||||
}, interval ?? 5000)
|
||||
}
|
||||
|
||||
async function loop() {
|
||||
if (!isActive.value) {
|
||||
return
|
||||
}
|
||||
|
||||
if (max !== -1 && times >= max) {
|
||||
return
|
||||
}
|
||||
times += 1
|
||||
|
||||
await fn()
|
||||
start()
|
||||
}
|
||||
|
||||
function resume() {
|
||||
if (!isActive.value) {
|
||||
isActive.value = true
|
||||
immediate ? loop() : start()
|
||||
}
|
||||
}
|
||||
|
||||
function pause() {
|
||||
isActive.value = false
|
||||
}
|
||||
|
||||
function reset() {
|
||||
clear()
|
||||
isActive.value = false
|
||||
times = 0
|
||||
}
|
||||
|
||||
if (immediate) {
|
||||
resume()
|
||||
}
|
||||
|
||||
if (getCurrentScope()) {
|
||||
onScopeDispose(pause)
|
||||
}
|
||||
|
||||
return {
|
||||
isActive,
|
||||
pause,
|
||||
resume,
|
||||
reset,
|
||||
}
|
||||
}
|
||||
@@ -1926,5 +1926,7 @@
|
||||
"刚刚刷新": "刚刚刷新",
|
||||
"大于1小时前刷新": "大于1小时前刷新",
|
||||
"x分钟前刷新": "{time}分钟前刷新",
|
||||
"注:已存在服务实例的主机,不能再勾选": "注:已存在服务实例的主机,不能再勾选"
|
||||
"注:已存在服务实例的主机,不能再勾选": "注:已存在服务实例的主机,不能再勾选",
|
||||
"版本更新": "版本更新",
|
||||
"建议「刷新页面」体验新的特性,「暂不刷新」可能会遇到未知异常,可手动刷新解决。": "建议「刷新页面」体验新的特性,「暂不刷新」可能会遇到未知异常,可手动刷新解决。"
|
||||
}
|
||||
|
||||
@@ -1919,5 +1919,7 @@
|
||||
"刚刚刷新": "Last refreshed just now",
|
||||
"大于1小时前刷新": "Last refreshed more than 1h ago",
|
||||
"x分钟前刷新": "Last refreshed {time}m ago",
|
||||
"注:已存在服务实例的主机,不能再勾选": "Note: Hosts with existing service instances cannot be selected"
|
||||
"注:已存在服务实例的主机,不能再勾选": "Note: Hosts with existing service instances cannot be selected",
|
||||
"版本更新": "Version Update",
|
||||
"建议「刷新页面」体验新的特性,「暂不刷新」可能会遇到未知异常,可手动刷新解决。": "It is recommended to \"Refresh Page\" to experience new features. \"Skip Refresh\" may cause unexpected errors. You can also \"Refresh Manually\" to resolve issues."
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import cmdbSearchComponent from './components/search/index'
|
||||
import routerActions from './router/actions'
|
||||
import tools from './utils/tools'
|
||||
import { gotoLoginPage } from '@/utils/login-helper'
|
||||
import { watchVersion } from '@/utils/check-version'
|
||||
import clipboard from 'vue-clipboard2'
|
||||
import './magicbox'
|
||||
import './directives'
|
||||
@@ -60,6 +61,10 @@ api.get(`${window.API_HOST}is_login`).then(() => {
|
||||
return !subEnv ? <App /> : <IframeEntry />
|
||||
}
|
||||
})
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
watchVersion()
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
gotoLoginPage()
|
||||
|
||||
134
src/ui/src/utils/check-version.js
Normal file
134
src/ui/src/utils/check-version.js
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making 蓝鲸 available.
|
||||
* Copyright (C) 2017 Tencent. All rights reserved.
|
||||
* Licensed under the MIT License (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under
|
||||
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { bkInfoBox } from 'bk-magic-vue'
|
||||
import i18n from '@/i18n'
|
||||
import useTimeoutPoll from '@/hooks/use-timeout-poll'
|
||||
|
||||
const STATIC_PATH = `${window.Site.publicPath ?? '/'}static/`
|
||||
const VERSION_FILE = 'build-hash.txt'
|
||||
|
||||
let localVersion = ''
|
||||
let isShown = false
|
||||
let dialog = null
|
||||
let checkVersionPoll = null
|
||||
|
||||
const checkVersionChannel = new BroadcastChannel('check-version')
|
||||
checkVersionChannel.addEventListener('message', ({ data }) => {
|
||||
if (data?.type !== 'toggle') {
|
||||
return
|
||||
}
|
||||
|
||||
// 共享弹出状态,视之后有无需求多个标签页只允许一次弹出提示使用
|
||||
// isShown = data.payload.isShown;
|
||||
})
|
||||
|
||||
const hideDialog = (silent = false) => {
|
||||
if (dialog) {
|
||||
dialog.close()
|
||||
}
|
||||
|
||||
isShown = false
|
||||
dialog = null
|
||||
|
||||
checkVersionPoll.resume()
|
||||
|
||||
if (!silent) {
|
||||
checkVersionChannel.postMessage({ type: 'toggle', payload: { isShown: false } })
|
||||
}
|
||||
}
|
||||
|
||||
const fetchBuildHash = async () => {
|
||||
const response = await fetch(`${STATIC_PATH}${VERSION_FILE}?_=${Date.now()}`)
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to get build hash')
|
||||
}
|
||||
|
||||
const newVersion = await response.text()
|
||||
|
||||
// localVersion 还不存在,认为是第一次加载
|
||||
if (!localVersion) {
|
||||
localVersion = newVersion
|
||||
}
|
||||
|
||||
if (newVersion !== localVersion) {
|
||||
showVersionDialog(newVersion)
|
||||
}
|
||||
}
|
||||
|
||||
const checkVersion = async () => {
|
||||
try {
|
||||
await fetchBuildHash()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleVisibilityChange = () => {
|
||||
if (!isShown && document.visibilityState === 'visible') {
|
||||
checkVersionPoll.resume()
|
||||
} else {
|
||||
checkVersionPoll.pause()
|
||||
}
|
||||
}
|
||||
|
||||
const showVersionDialog = (newVersion) => {
|
||||
if (isShown) {
|
||||
return
|
||||
}
|
||||
|
||||
dialog = bkInfoBox({
|
||||
width: 650,
|
||||
maskClose: false,
|
||||
title: i18n.t('版本更新'),
|
||||
subTitle: i18n.t('建议「刷新页面」体验新的特性,「暂不刷新」可能会遇到未知异常,可手动刷新解决。'),
|
||||
okText: i18n.t('刷新'),
|
||||
cancelText: i18n.t('取消'),
|
||||
confirmFn: () => {
|
||||
window.location.reload()
|
||||
},
|
||||
cancelFn: () => {
|
||||
hideDialog()
|
||||
},
|
||||
})
|
||||
|
||||
if (dialog) {
|
||||
isShown = true
|
||||
localVersion = newVersion
|
||||
checkVersionPoll.pause()
|
||||
checkVersionChannel.postMessage({ type: 'toggle', payload: { isShown: true } })
|
||||
}
|
||||
}
|
||||
|
||||
const addVisibilityChangeListener = () => {
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange)
|
||||
}
|
||||
|
||||
const removeVisibilityChangeListener = () => {
|
||||
document.removeEventListener('visibilitychange', handleVisibilityChange)
|
||||
}
|
||||
|
||||
const addBeforeunloadListener = () => {
|
||||
window.addEventListener('beforeunload', () => {
|
||||
hideDialog()
|
||||
removeVisibilityChangeListener()
|
||||
checkVersionPoll.reset()
|
||||
checkVersionChannel.close()
|
||||
})
|
||||
}
|
||||
|
||||
export const watchVersion = () => {
|
||||
checkVersionPoll = useTimeoutPoll(checkVersion, 5 * 60 * 1000, { immediate: true, max: -1 })
|
||||
addVisibilityChangeListener()
|
||||
addBeforeunloadListener()
|
||||
}
|
||||
@@ -11,6 +11,8 @@
|
||||
*/
|
||||
|
||||
import { t } from '@/i18n'
|
||||
import xss from 'xss'
|
||||
import { BUILTIN_PASTE_SPLIT_FIELDS } from '@/dictionary/model-constants.js'
|
||||
|
||||
const hex2grb = (hex) => {
|
||||
const rgb = []
|
||||
@@ -343,3 +345,29 @@ export function* paginateIterator(list, pageSize) {
|
||||
index += pageSize
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤XSS攻击,只保留纯文本
|
||||
* @param {string} str 字符串
|
||||
* @returns 过滤后的字符串
|
||||
*/
|
||||
export function filterXSS(str) {
|
||||
if (!str) return str
|
||||
const result = xss(str, {
|
||||
whiteList: {},
|
||||
stripIgnoreTag: true,
|
||||
stripIgnoreTagBody: ['script']
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前字段是否分割字符串
|
||||
* @param {string} id 字段ID
|
||||
* @param {funtion} fn 自定义方法
|
||||
* @return true | false
|
||||
**/
|
||||
export function isPasteSplit(id, fn = () => false) {
|
||||
if (BUILTIN_PASTE_SPLIT_FIELDS.includes(id)) return true
|
||||
return fn?.()
|
||||
}
|
||||
|
||||
@@ -153,6 +153,9 @@
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isModelInstance() {
|
||||
this.condition.bk_obj_id = ''
|
||||
},
|
||||
instanceType() {
|
||||
this.condition.resource_id = ''
|
||||
this.condition.resource_name = ''
|
||||
|
||||
@@ -118,14 +118,13 @@
|
||||
|
||||
let result = []
|
||||
if (window.ESB.userManage) {
|
||||
const url = new URL(window.ESB.userManage)
|
||||
const params = {
|
||||
app_code: 'bk-magicbox',
|
||||
page: 1,
|
||||
page_size: 100,
|
||||
fuzzy_lookups: query
|
||||
}
|
||||
const api = `${window.API_HOST}proxy/get/usermanage${url.pathname}`
|
||||
const api = window.ESB.userManage
|
||||
const response = await jsonp(api, params)
|
||||
if (response.code !== 0) {
|
||||
console.error(response?.message)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user