From 4e169d6a47f340da512c9a736daea5705279861d Mon Sep 17 00:00:00 2001 From: wklken Date: Fri, 25 Oct 2024 15:40:47 +0800 Subject: [PATCH] feat(apigw_manager/drf): update to support multiple stages (#196) --- sdks/apigw-manager/docs/jwt-explain.md | 22 ++--- sdks/apigw-manager/docs/plugin-use-guide.md | 33 ++++--- .../docs/sync-apigateway-with-django.md | 26 +++--- .../docs/sync-apigateway-with-docker.md | 93 +++++++++++-------- sdks/apigw-manager/docs/sync_apigateway.md | 78 +++++++++------- sdks/apigw-manager/pyproject.toml | 2 +- .../management/commands/data/definition.yaml | 22 ++--- .../commands/generate_resources_yaml.py | 6 +- .../commands/sync_drf_apigateway.py | 6 +- .../commands/test_generate_resources_yaml.py | 4 +- 10 files changed, 164 insertions(+), 128 deletions(-) diff --git a/sdks/apigw-manager/docs/jwt-explain.md b/sdks/apigw-manager/docs/jwt-explain.md index 7e8fa23..0c64d6c 100644 --- a/sdks/apigw-manager/docs/jwt-explain.md +++ b/sdks/apigw-manager/docs/jwt-explain.md @@ -1,14 +1,14 @@ # jwt 说明 -后端服务接入网关时, 接口可能开启了应用认证/用户认证, 此时调用方需要传认证header头 `X-Bkapi-Authorization`, 网关认证通过后, 会生成一个 `X-Bkapi-JWT` 头给到后端服务, 里面包含了认证结果信息, 这是一个 jwt token +后端服务接入网关时,接口可能开启了应用认证/用户认证,此时调用方需要传认证 header 头 `X-Bkapi-Authorization`, 网关认证通过后,会生成一个 `X-Bkapi-JWT` 头给到后端服务,里面包含了认证结果信息,这是一个 jwt token ![img](./images/jwt.png) -## 如何获取网关公钥 +## 1. 如何获取网关公钥 后端服务如需解析 API 网关发送的请求头 X-Bkapi-JWT,需要提前获取该网关的公钥。获取网关公钥,有以下方案。 -### 1. 根据 SDK 提供的 Django Command 拉取 +### 1.1 根据 SDK 提供的 Django Command 拉取 在同步网关数据时,直接添加以下 Command 拉取网关公钥。网关公钥将保存在 model Context 对应的库表 apigw_manager_context 中,SDK 提供的 Django 中间件将从表中读取网关公钥。 @@ -20,14 +20,14 @@ python manage.py fetch_apigw_public_key python manage.py fetch_apigw_public_key --gateway-name my-gateway ``` -### 2. 直接获取网关公钥,配置到项目配置文件 +### 1.2 直接获取网关公钥,配置到项目配置文件 服务仅需接入一些固定的网关部署环境时,可在网关管理端,网关基本信息中查询网关公钥,并配置到项目配置文件。 蓝鲸官方网关,需要自动注册并获取网关公钥,可联系蓝鲸官方运营同学,在服务部署前,由官方提前创建网关,并设置网关公钥、私钥,同时将网关公钥同步给后端服务。 具体可参考 helm-charts 仓库的 README。 -### 3. 通过网关公开接口,拉取网关公钥 +### 1.3 通过网关公开接口,拉取网关公钥 API 网关提供了公钥查询接口,后端服务可按需根据接口拉取网关公钥,接口信息如下: @@ -53,9 +53,9 @@ curl -X GET 'https://bkapi.example.com/api/bk-apigateway/prod/api/v1/apis/{gatew - 拉取公钥时,不能实时拉取,需要添加缓存(实时拉取会导致整体接口性能下降) -## 校验请求来自 API 网关 +## 2. 校验请求来自 API 网关 -### 场景一:Django 项目 +### 2.1 场景一:Django 项目 要在后端服务中认证 API 网关传递过来的请求头 `X-Bkapi-JWT`,可以通过在 settings 中的 MIDDLEWARE 中添加以下 Django 中间件。这样,在请求处理过程中,会自动解析请求头中的 X-Bkapi-JWT,并将相关信息添加到 request 对象中。 @@ -86,7 +86,7 @@ AUTHENTICATION_BACKENDS += [ - SDK model Context (库表 apigw_manager_context),需提前执行 `python manage.py fetch_apigw_public_key` 拉取并保存网关公钥 -- settings.APIGW_PUBLIC_KEY,可在网关基本页面/API公钥通过点击`复制`按钮或者`下载`获取公钥,并配置到 settings 中。 +- settings.APIGW_PUBLIC_KEY,可在网关基本页面/API 公钥通过点击`复制`按钮或者`下载`获取公钥,并配置到 settings 中。 > 公钥示例: > ```shell @@ -113,7 +113,7 @@ AUTHENTICATION_BACKENDS += [ ##### ApiGatewayJWTUserMiddleware -根据 `request.jwt`,在 `request` 中注入 `user` 对象: +根据 `request.jwt`,在 `request` 中注入 `user` 对象: - 如果用户通过认证:其为一个 Django User Model 对象,用户名为当前请求用户的用户名 - 如果用户未通过认证,其为一个 Django AnonymousUser 对象,用户名为当前请求用户的用户名 @@ -157,7 +157,7 @@ APIGW_MANAGER_DUMMY_PAYLOAD_APP_CODE # JWT payload 中的 app_code APIGW_MANAGER_DUMMY_PAYLOAD_USERNAME # JWT payload 中的 username ``` -### 场景二:非 Django 项目 +### 2.2 场景二:非 Django 项目 非 Django 项目,需要项目获取网关公钥,并解析请求头中的 X-Bkapi-JWT;获取网关公钥的方案请参考上文。 @@ -188,4 +188,4 @@ APIGW_MANAGER_DUMMY_PAYLOAD_USERNAME # JWT payload 中的 username "nbf": 1701399303, # Not Before 时间 "iss": "APIGW" # 签发者 } -``` \ No newline at end of file +``` diff --git a/sdks/apigw-manager/docs/plugin-use-guide.md b/sdks/apigw-manager/docs/plugin-use-guide.md index 88e78da..1083135 100644 --- a/sdks/apigw-manager/docs/plugin-use-guide.md +++ b/sdks/apigw-manager/docs/plugin-use-guide.md @@ -1,10 +1,13 @@ # 插件配置说明 + 插件配置支持主要在 `stage`(definition.yaml) 和 `resource`(resource.yaml) 两个维度上,资源配置的插件优先级最高。 -> 注意:所有配置均以 yaml 配置同步为主,举例来说: 如果通过yaml配置的插件配置则会覆盖掉用户在网关管理页面创建的插件配置,如果 yaml 没有配置该插件,则也不会移除 -> 用户之前在页面创建的插件配置,不过 yaml 如果没有配置上一次yaml配置的插件,则会移除上一次 yaml 配置的插件。 -> `CORS` 插件和 `IP 访问保护插件` 不推荐在yaml配置绑定在环境上。 +> 注意:所有配置均以 yaml 配置同步为主,举例来说:如果通过 yaml 配置的插件配置则会覆盖掉用户在网关管理页面创建的插件配置,如果 yaml 没有配置该插件,则也不会移除 +> 用户之前在页面创建的插件配置,不过 yaml 如果没有配置上一次 yaml 配置的插件,则会移除上一次 yaml 配置的插件。 +> `CORS` 插件和 `IP 访问保护插件` 不推荐在 yaml 配置绑定在环境上。 + +## 1. 跨域资源共享(CORS)插件 -## 跨域资源共享(CORS)插件 +### 1.1 参数说明 | 参数 | 类型 | 默认值 | 描述 | | ---------------- | ------ | ------ | ------------------------------------------------------------ | @@ -15,7 +18,7 @@ | max_age | 整数 | 86400 | 浏览器缓存 CORS 结果的最大时间,单位为秒。 | | allow_credential | 布尔值 | true | 是否允许跨域访问的请求方携带凭据(如 Cookie 等)。 | -### 配置例子 +### 1.2 配置例子 ```yaml - type: bk-cors @@ -28,14 +31,16 @@ allow_credential: false ``` -## Header 转换插件 +## 2. Header 转换插件 + +### 2.1 参数说明 | 参数 | 类型 | 默认值 | 描述 | | ------ | ---- | ------ | ---------------------------- | | set | 数组 | [] | 设置的请求头,包括键和值。 | | remove | 数组 | [] | 删除的请求头,只需要提供键。 | -### 配置例子 +### 2.2 配置例子 ```yaml - type: bk-header-rewrite @@ -46,7 +51,9 @@ remove: [] ``` -## IP 访问保护插件 +## 3. IP 访问保护插件 + +### 3.1 参数说明 | 参数 | 类型 | 默认值 | 描述 | | --------- | ------ | ------ | -------------------------------------- | @@ -54,7 +61,7 @@ | blacklist | 字符串 | "" | 黑名单中的 IP 地址,支持 CIDR 表示法。 | | message | 字符串 | "" | 当 IP 地址不被允许时显示的消息。 | -### 配置例子 +### 3.2 配置例子 ```yaml - type: bk-ip-restriction @@ -64,16 +71,18 @@ message: 'Your IP is not allowed' ``` -## 频率控制插件 +## 4. 频率控制插件 + +### 4.1 参数说明 | 参数 | 类型 | 默认值 | 描述 | | ----------- | ------ | ------ | ------------------------------------------------ | | rates | 对象 | {} | 包含默认频率限制和特殊应用频率限制的对象。 | | default | 对象 | {} | 单个应用的默认频率限制,包括次数和时间范围。 | | specials | 数组 | [] | 特殊应用频率限制,对指定应用设置单独的频率限制。 | -| bk_app_code | 字符串 | "" | 蓝鲸应用ID,用于特殊应用频率限制。 | +| bk_app_code | 字符串 | "" | 蓝鲸应用 ID,用于特殊应用频率限制。 | -### 配置例子 +### 4.2 配置例子 ```yaml - type: bk-rate-limit diff --git a/sdks/apigw-manager/docs/sync-apigateway-with-django.md b/sdks/apigw-manager/docs/sync-apigateway-with-django.md index 7a272de..77a8e97 100644 --- a/sdks/apigw-manager/docs/sync-apigateway-with-django.md +++ b/sdks/apigw-manager/docs/sync-apigateway-with-django.md @@ -1,11 +1,13 @@ -### 直接使用 Django Command 同步网关 +# 直接使用 Django Command 同步网关 项目安装 SDK apigw-manager 后,可以直接执行 SDK 提供的 Django Command。 + - 准备文件的样例 [examples/django/use-custom-script](../examples/django/use-custom-script) -#### 步骤1. 准备自定义同步脚本 +## 步骤 1. 准备自定义同步脚本 创建一个 bash 脚本,如 `sync-apigateway.sh`,使用 SDK 提供的 Django Command 定义网关配置的同步脚本,样例如下: + ```shell #!/bin/bash @@ -31,7 +33,7 @@ python manage.py sync_apigw_config --gateway-name=${gateway_name} --file="${defi python manage.py sync_apigw_stage --gateway-name=${gateway_name} --file="${definition_file}" # 同步网关资源 -# +# # --delete: 当资源在服务端存在,却未出现在资源定义文件中时,指定本参数会强制删除这类资源,以保证服务端资源和文件内容完全一致。 # 如果未指定本参数,将忽略未出现的资源 # --doc_language: en/zh 是否生成接口文档(中文/英文) @@ -54,6 +56,7 @@ echo "gateway sync definition end" ``` 如果需要更灵活的控制,也可以采用自定义 Django Command 的方案,例如: + ```python from django.conf import settings from django.core.management.base import BaseCommand @@ -64,7 +67,7 @@ class Command(BaseCommand): def handle(self, *args, **kwargs): if not getattr(settings, "SYNC_APIGATEWAY_ENABLED", True): return - + # 待同步网关名,需修改为实际网关名;直接指定网关名,则不需要配置 Django settings BK_APIGW_NAME gateway_name = "bk-demo" @@ -81,15 +84,15 @@ class Command(BaseCommand): call_command("fetch_apigw_public_key", f"--gateway-name={gateway_name}") ``` -#### 步骤2. 添加 SDK apigw-manager +## 步骤 2. 添加 SDK apigw-manager 将 SDK apigw-manager 添加到项目依赖中,如 pyproject.toml 或 requirements.txt。 -#### 步骤3. 将准备的网关配置文件,添加到项目 +## 步骤 3. 将准备的网关配置文件,添加到项目 将准备的网关配置文件:definition.yaml, resources.yaml, apidocs (可选),添加到项目 -#### 步骤4. 更新 Django settings 配置 +## 步骤 4. 更新 Django settings 配置 在 Django settings.py 中定义网关名称和接口地址模板: @@ -104,7 +107,7 @@ BK_APIGW_NAME = "my-gateway-name" # 需将 bkapi.example.com 替换为真实的云 API 域名; # 在 PaaS 3.0 部署的应用,可从环境变量中获取 BK_API_URL_TMPL,不需要额外配置; # 实际上,SDK 将调用网关 bk-apigateway 接口将数据同步到 API 网关 -BK_API_URL_TMPL = "http://bkapi.example.com/api/{api_name}/" ## 例如:网关host是:`bkapi.example.com`,则对应的值为:http://bkapi.example.com/api/{api_name} 注意:{api_name} 这个是占位符。 +BK_API_URL_TMPL = "http://bkapi.example.com/api/{api_name}/" ## 例如:网关 host 是:`bkapi.example.com`,则对应的值为:http://bkapi.example.com/api/{api_name} 注意:{api_name} 这个是占位符。 ``` 在 INSTALLED_APPS 中加入以下配置,SDK 将创建表 `apigw_manager_context` 用于存储一些中间数据: @@ -115,9 +118,10 @@ INSTALLED_APPS += [ ] ``` -#### 步骤5. 同步网关数据到 API 网关 +## 步骤 5. 同步网关数据到 API 网关 chart 部署方案,可采用 K8S Job 同步,样例如下: + ```yaml apiVersion: batch/v1 kind: Job @@ -140,12 +144,12 @@ spec: ``` 二进制部署方案,在部署阶段直接执行 sync-apigateway.sh 脚本: + ```shell bash support-files/bin/sync-apigateway.sh ``` - -### 支持的 Django Command 列表 +## 支持的 Django Command 列表 ```bash # 可选,为网关添加关联应用,关联应用可以通过网关 bk-apigateway 提供的接口管理网关数据 diff --git a/sdks/apigw-manager/docs/sync-apigateway-with-docker.md b/sdks/apigw-manager/docs/sync-apigateway-with-docker.md index d721c74..95077d7 100644 --- a/sdks/apigw-manager/docs/sync-apigateway-with-docker.md +++ b/sdks/apigw-manager/docs/sync-apigateway-with-docker.md @@ -1,21 +1,23 @@ -## 通过镜像方式同步网关 +# 通过镜像方式同步网关 -#### 镜像说明 +## 镜像说明 网关提供基础镜像 apigw-manager,用于同步网关数据到 API 网关。基础镜像通过 [Dockerfile](../Dockerfile) 进行构建,该镜像封装了 [demo](../demo) 项目,可读取 /data/ 目录,直接进行网关注册和同步操作,目录约定: + - */data/definition.yaml*:网关定义文件,用于注册网关; - */data/resources.yaml*:资源定义文件,用于同步网关资源,可通过网关导出; - */data/apidocs*:文档目录,可通过网关导出后解压; - */data/bin/sync-apigateway.sh*:自定义同步脚本; - 镜像执行同步时,需要额外的环境变量支持: + - `BK_APIGW_NAME`:网关名称; -- `BK_API_URL_TMPL`:云网关 API 地址模板,例如:网关host是:`bkapi.example.com`,则对应的值为:http://bkapi.example.com/api/{api_name} 注意:{api_name} 这个是占位符。 +- `BK_API_URL_TMPL`:云网关 API 地址模板,例如:网关 host 是:`bkapi.example.com`,则对应的值为:http://bkapi.example.com/api/{api_name} 注意:{api_name} 这个是占位符。 - `BK_APP_CODE`:应用名称; - `BK_APP_SECRET`:应用密钥; 通过镜像进行同步时,镜像需访问用户自定义的数据,在 chart 和二进制两种不同的部署方案中,镜像加载用户自定义数据的方式有所不同: + - chart: - 单文件大小 < 1MB 时,可使用 ConfigMap 挂载 - 单文件大小 >= 1MB 时,可创建自定义镜像 @@ -28,10 +30,9 @@ - `APPLY_APIGW_PERMISSIONS_ARGS`: 用于命令 `apply_apigw_permissions`,申请网关资源权限 - `GRANT_APIGW_PERMISSIONS_ARGS`: 用于命令 `grant_apigw_permissions`,授权网关权限 - `SYNC_APIGW_RESOURCES_ARGS`: 默认值:"--delete",用于命令 `sync_apigw_resources`,同步网关资源 -- `SYNC_RESOURCE_DOCS_BY_ARCHIVE_ARGS`: 默认值: "--safe-mode",用于命令 `sync_resource_docs_by_archive`,同步网关文档 +- `SYNC_RESOURCE_DOCS_BY_ARCHIVE_ARGS`: 默认值: "--safe-mode",用于命令 `sync_resource_docs_by_archive`,同步网关文档 - `CREATE_VERSION_AND_RELEASE_APIGW_ARGS`: 默认值:"--generate-sdks",用于命令 `create_version_and_release_apigw`,创建资源版本并发布 - 基础镜像提供了一些自定义同步脚本常用的 bash 函数,以及执行 Django Command 指令的辅助脚本: - `functions.sh`,定义一些常用 bash 函数,源码 [/apigw-manager/bin/functions.sh](../bin/functions.sh) @@ -46,8 +47,7 @@ functions.sh 中的 bash 函数: - `log_warn`: 打印 warning 日志 - `log_error`: 打印 error 日志 - -#### 准备工作 +## 准备工作 准备自定义同步脚本,镜像执行时指定使用自定义同步脚本即可。自定义同步脚本样例 [sync-apigateway.sh](../examples/chart/use-custom-docker-image/my-apigw-manager/support-files/bin/sync-apigateway.sh) 如下: @@ -72,7 +72,8 @@ call_command_or_warning migrate apigw title "syncing apigateway" call_definition_command_or_exit sync_apigw_config "${definition_file}" --gateway-name=${gateway_name} call_definition_command_or_exit sync_apigw_stage "${definition_file}" --gateway-name=${gateway_name} -# --doc_language: en/zh 是否生成接口文档(中文/英文) + +# 可指定 --doc_language=en/zh 是否生成接口文档(中文/英文) call_definition_command_or_exit sync_apigw_resources "${resources_file}" --gateway-name=${gateway_name} --delete call_definition_command_or_exit sync_resource_docs_by_archive "${definition_file}" --gateway-name=${gateway_name} --safe-mode call_definition_command_or_exit grant_apigw_permissions "${definition_file}" --gateway-name=${gateway_name} @@ -88,16 +89,19 @@ call_definition_command_or_exit create_version_and_release_apigw "${definition_f log_info "done" ``` +## 同步方式 -#### 使用方式一:chart + ConfigMap +### 1. 使用方式一:chart + ConfigMap 使用基础镜像 apigw-manager,并为网关配置、资源文档创建 ConfigMap 对象,将这些 ConfigMap 挂载到基础镜像中,如此镜像就可以读取到网关数据,但是 chart 本身限制单文件不能超过 1MB。 + - 准备文件的样例 [examples/chart/use-configmap](../examples/chart/use-configmap) 操作步骤如下: -步骤1:将网关配置、资源文档、自定义同步命令,放到 chart 项目的 files 文件夹下,可参考目录: -``` +步骤 1:将网关配置、资源文档、自定义同步命令,放到 chart 项目的 files 文件夹下,可参考目录: + +```plain . ├── Chart.yaml ├── files @@ -113,7 +117,8 @@ log_info "done" │   └── resources.yaml ``` -步骤2:在 chart values.yaml 中添加配置 +步骤 2:在 chart values.yaml 中添加配置 + ```yaml apigatewaySync: image: "hub.bktencent.com/blueking/apigw-manager:3.1.1" @@ -141,7 +146,8 @@ apigatewaySync: value: "http://bkapi.example.com/api/{api_name}" ``` -步骤2:在 chart templates 下创建 ConfigMap 模板文件,样例如下: +步骤 2:在 chart templates 下创建 ConfigMap 模板文件,样例如下: + ```yaml {{- $files := .Files }} {{- range $item := .Values.apigatewaySync.configMapMounts }} @@ -155,7 +161,8 @@ data: {{- end }} ``` -步骤3:添加 K8S Job 同步任务,样例如下: +步骤 3:添加 K8S Job 同步任务,样例如下: + ```yaml apiVersion: batch/v1 kind: Job @@ -174,12 +181,12 @@ spec: name: sync-apigateway env: {{- toYaml .Values.apigatewaySync.extraEnvVars | nindent 8 }} - volumeMounts: + volumeMounts: {{- range $item := .Values.apigatewaySync.configMapMounts }} - mountPath: "{{ $item.mountPath }}" name: "{{ $item.name }}" {{- end }} - volumes: + volumes: {{- range $item := .Values.apigatewaySync.configMapMounts }} - name: "{{ $item.name }}" configMap: @@ -189,15 +196,17 @@ spec: restartPolicy: Never ``` -#### 使用方式二:chart + 自定义镜像 +### 2. 使用方式二:chart + 自定义镜像 可将 apigw-manager 作为基础镜像,将配置文件和文档一并构建成一个新镜像,然后通过如 K8S Job 方式进行同步。 + - 准备文件的样例 [examples/chart/use-custom-docker-image](../examples/chart/use-custom-docker-image) 操作步骤如下: -步骤1. 将网关配置,资源文档,自定义同步命令,放到一个文件夹下,可参考目录: -``` +步骤 1. 将网关配置,资源文档,自定义同步命令,放到一个文件夹下,可参考目录: + +```plain . ├── Dockerfile └── support-files @@ -212,19 +221,22 @@ spec: └── resources.yaml ``` -步骤2. 构建 Dockerfile,参考: +步骤 2. 构建 Dockerfile,参考: + ```Dockerfile FROM hub.bktencent.com/blueking/apigw-manager:3.1.1 COPY support-files /data/ ``` -步骤3:构建新镜像 +步骤 3:构建新镜像 + ```shell docker build -t my-apigw-manager -f Dockerfile . ``` -步骤4:添加 K8S Job 同步任务,样例如下 +步骤 4:添加 K8S Job 同步任务,样例如下 + ```yaml apiVersion: batch/v1 kind: Job @@ -253,9 +265,10 @@ spec: restartPolicy: Never ``` -#### 使用方式三:二进制 + 外部文件挂载 +### 3. 使用方式三:二进制 + 外部文件挂载 使用基础镜像 apigw-manager,通过外部文件挂载的方式,将对应的目录挂载到 /data/ 目录下,可通过以下类似的命令进行同步: + ```bash docker run --rm \ -v //:/data/ \ @@ -266,40 +279,38 @@ docker run --rm \ hub.bktencent.com/blueking/apigw-manager:3.1.1 ``` - -### 支持同步指令 +## 支持同步指令 ```bash # 可选,为网关添加关联应用,关联应用可以通过网关 bk-apigateway 提供的接口管理网关数据 call_definition_command_or_exit add_related_apps "${definition_file}" --gateway-name=${gateway_name} -# 可选,申请网关权限 -call_definition_command_or_exit apply_apigw_permissions "${definition_file}" --gateway-name=${gateway_name} - +# 可选,申请网关权限 +call_definition_command_or_exit apply_apigw_permissions "${definition_file}" --gateway-name=${gateway_name} + # 创建资源版本并发布;指定参数 --generate-sdks 时,会同时生成资源版本对应的网关 SDK, 指定 --stage stage1 stage2 时会发布指定环境,不设置则发布所有环境 # 指定参数 --no-pub 则只生成版本,不发布 call_definition_command_or_exit create_version_and_release_apigw "${definition_file}" --gateway-name=${gateway_name} - -# 获取网关公钥,存放到文件 apigateway.pub -apigw-manager.sh fetch_apigw_public_key --gateway-name=${gateway_name} --print > "apigateway.pub" - + +# 获取网关公钥,存放到文件 apigateway.pub +apigw-manager.sh fetch_apigw_public_key --gateway-name=${gateway_name} --print > "apigateway.pub" + # 可选,为应用主动授权 -call_definition_command_or_exit grant_apigw_permissions "${definition_file}" --gateway-name=${gateway_name} +call_definition_command_or_exit grant_apigw_permissions "${definition_file}" --gateway-name=${gateway_name} # 同步网关基本信息 call_definition_command_or_exit sync_apigw_config "${definition_file}" --gateway-name=${gateway_name} - + # 同步网关资源 -# +# # --delete: 当资源在服务端存在,却未出现在资源定义文件中时,指定本参数会强制删除这类资源,以保证服务端资源和文件内容完全一致。 # 如果未指定本参数,将忽略未出现的资源 # --doc_language: en/zh 是否生成接口文档(中文/英文) -call_definition_command_or_exit sync_apigw_resources "${resources_file}" --gateway-name=${gateway_name} --delete - +call_definition_command_or_exit sync_apigw_resources "${resources_file}" --gateway-name=${gateway_name} --delete --doc_language=zh + # 同步网关环境信息 -call_definition_command_or_exit sync_apigw_stage "${definition_file}" --gateway-name=${gateway_name} - +call_definition_command_or_exit sync_apigw_stage "${definition_file}" --gateway-name=${gateway_name} + # 可选,同步资源文档 call_definition_command_or_exit sync_resource_docs_by_archive "${definition_file}" --gateway-name=${gateway_name} --safe-mode - ``` diff --git a/sdks/apigw-manager/docs/sync_apigateway.md b/sdks/apigw-manager/docs/sync_apigateway.md index 6f94302..4c42a5d 100644 --- a/sdks/apigw-manager/docs/sync_apigateway.md +++ b/sdks/apigw-manager/docs/sync_apigateway.md @@ -1,12 +1,11 @@ - # 根据 YAML 同步网关配置 -SDK 同步网关配置到 API 网关,支持多种方案: +SDK 同步网关配置到 API 网关,支持多种方案: - [直接使用 Django Command 同步](./sync-apigateway-with-django.md):此方案适用于 Django 项目;Django 项目,可直接执行 SDK 提供的 Django Command 指令 - [通过镜像方式同步](./sync-apigateway-with-docker.md):此方案适用于非 Django 项目;非 Django 项目,无法直接执行 SDK 提供的 Django Command 指令 -## 准备工作 +## 1. 准备工作 同步网关配置到 API 网关,需要准备网关配置、资源配置、资源文档、自定义同步脚本等数据,可参考目录: @@ -24,7 +23,7 @@ support-files └── anything.md ``` -## 1. definition.yaml +### 1.1 definition.yaml 用于定义网关、环境等配置,为了简化使用,使用以下模型进行处理: @@ -44,19 +43,20 @@ support-files ``` definition.yaml 中可以使用 Django 模版语法引用和渲染变量,内置以下变量: + - `settings`:Django 提供的配置对象,优先适合用于使用 Django Command 同步 - `environ`:环境变量,推荐镜像同步方式使用 推荐在一个文件中统一进行定义,用命名空间区分不同配置间的定义,definition.yaml 样例: -目前有两种配置文件版本:spec_version=1/2,主要区别就是stage相关的配置方式上有一些不一样。 -新接入系统请使用 spec_version=2, 旧有系统如果需要配置多个stage/配置多个backend, 建议也升级到spec_version=2并变更相关yaml配置。 +目前有两种配置文件版本:spec_version=1/2,主要区别就是 stage 相关的配置方式上有一些不一样。 +新接入系统请使用 spec_version=2,旧有系统如果需要配置多个 stage/配置多个 backend,建议也升级到 spec_version=2 并变更相关 yaml 配置。 区别如下: spec_version: 1 ```yaml # definition.yaml 配置文件版本号,必填,固定值 1/2 -# 1:key为stage; 只支持单个 stage, 并且 proxy_http只能配置一个后端服务 +# 1:key 为 stage; 只支持单个 stage,并且 proxy_http 只能配置一个后端服务 spec_version: 1 stage: name: "prod" @@ -75,7 +75,7 @@ spec_version: 2 ```yaml # definition.yaml 配置文件版本号,必填,固定值 1/2 -# 2:key为stages; 支持多个stages,并且每个stage可以配置多个backend后端服务 +# 2:key 为 stages; 支持多个 stages,并且每个 stage 可以配置多个 backend 后端服务 spec_version: 2 stages: - name: "prod" @@ -100,14 +100,14 @@ stages: - host: "http://httpbin.org" weight: 100 ``` -> 📢 注意:如果之前接入过的,建议将 sepc_version改成 2,并将原先 `stage:{}`改成 `stages: []` +> 📢 注意:如果之前接入过的,建议将 sepc_version 改成 2,并将原先 `stage:{}`改成 `stages: []` 整体的样例: ```yaml # definition.yaml 配置文件版本号,必填,固定值 1/2 -spec_version: 2 # 如果之前接入过的,建议将 sepc_version改成 2,并将原先 stage:改成 stages: [] +spec_version: 2 # 如果之前接入过的,建议将 sepc_version 改成 2,并将原先 stage:改成 stages: [] # 定义发布内容,用于命令 `create_version_and_release_apigw`,具体生效的环境取决于参数 --stage prod test 等方式来指定 release: @@ -142,10 +142,10 @@ stages: # vars: # key: "value" # 代理配置 - # proxy_http 与 backends 二选一, 推荐使用 backends 方式配置 - # 网关版本 <= 1.13.3, 只支持一个后端服务, 默认是 default - # 网关版本 1.13.3之后引入 backends 配置方式,支持多后端服务 - # 注意: 资源中引用的 backend 一定要配置, 否则会导入失败,不配置则会选 择 default 后端服务 + # proxy_http 与 backends 二选一,推荐使用 backends 方式配置 + # 网关版本 <= 1.13.3, 只支持一个后端服务,默认是 default + # 网关版本 1.13.3 之后引入 backends 配置方式,支持多后端服务 + # 注意: 资源中引用的 backend 一定要配置,否则会导入失败,不配置则会选 择 default 后端服务 # 如果 backends 没有配置 default 且 resource 未指定 backend 则会导致版本发布校验失败 backends: - name: "default" @@ -198,10 +198,10 @@ grant_permissions: # gateway: 按网关授权,包括网关下所有资源,以及未来新创建的资源 # resource: 按资源维度授权 grant_dimension: "gateway" - # 如果是按照 resource 维度授权,需要提供如下的具体resource_name + # 如果是按照 resource 维度授权,需要提供如下的具体 resource_name # resource_names: - # - resource_name_1 - # - resource_name_2 + # - resource_name_1 + # - resource_name_2 # 应用申请指定网关所有资源的权限,待网关管理员审批后,应用才可访问网关资源; # 用于命令 `apply_apigw_permissions` @@ -230,10 +230,12 @@ resource_docs: ``` **注意:** + - 同步资源或者环境相关配置后,需要创建版本并发布才能生效,发布数据定义于 definition.yaml `release` - 资源配置 resources.yaml 变更时,需要更新 definition.yaml `release` 中的版本号 version,以便正确创建资源版本及 SDK - 详细的插件配置见:[插件配置说明](./plugin-use-guide.md) -## 2. resources.yaml + +### 1.2 resources.yaml 用于定义资源配置,建议通过网关管理端导出。为了方便用户直接使用网关导出的资源文件,资源定义默认没有命名空间。 @@ -241,16 +243,18 @@ resource_docs: > 详细的插件配置见:[插件配置说明](./plugin-use-guide.md) -## 3. apidocs(可选) +### 1.3 apidocs(可选) 资源文档,资源文档为 markdown 格式。资源文档的文件名,应为 `资源名称` + `.md` 格式,假如资源名称为 get_user,则文档文件名应为 get_user.md。 将资源的中文文档放到目录 `zh` 下,英文文档放到目录 `en` 下,如果某语言文档不存在,可忽略对应目录。 -### 文档目录及命名 +#### 1.3.1 文档目录及命名 + 资源文档为 markdown 格式,文件名,应为 资源名称 + .md 格式,例如:资源名称为 get_user 时,则其文档文件名应为 get_user.md 文档文件目录样例如下: -``` + +```plain . ├── en │ ├── create_user.md @@ -259,9 +263,10 @@ resource_docs: ├── create_user.md └── get_user.md ``` -### 在资源文档中引用公共文档片段 -使用jinja2 include 复用公共部分 +#### 1.3.2 在资源文档中引用公共文档片段 + +使用 jinja2 include 复用公共部分 - 网关采用 Jinja 模板 支持文档文件的引用。对于需采用 Jinja 模板渲染的资源文档,需将文件名后缀设置为 .md.j2 - 对于被引用的公共文档片段,文件名可以以下划线(_)开头。 @@ -277,6 +282,7 @@ resource_docs: {# 引用公共文档片段 _user_model.md.j2 #} {% include '_user_model.md.j2' %} ``` + 资源文档中包含 Jinja 模板文件时,文档的目录结构示例如下: ```shell @@ -291,9 +297,9 @@ resource_docs: └── _user_model.md.j2 ``` - 导入资源文档时,可以直接使用资源文档归档文件,也可以使用资源文档目录。参考上文 definition.yaml 样例, 在项目 definition.yaml 文件中,修改资源文档相关配置 resource_docs: + ```yaml resource_docs: # 资源文档的归档文件,可为 tar.gz,zip 格式文件;创建归档文件可使用指令 `tar czvf xxx.tgz en zh` @@ -303,9 +309,10 @@ resource_docs: basedir: "support-files/apidocs/" ``` -## 4. 国际化支持 +### 1.4 国际化支持 + +#### 1.4.1 网关描述国际化 -### 网关描述国际化 在 definition.yaml 中利用字段 description_en 指定英文描述。样例: ```yaml @@ -316,7 +323,9 @@ apigateway: maintainers: - "admin" ``` -### 环境描述国际化 + +#### 1.4.2 环境描述国际化 + 在 definition.yaml 中利用字段 description_en 指定英文描述。样例: ```yaml @@ -325,8 +334,11 @@ stage: description: "xxx" description_en: "This is the English description" ``` -### 资源描述国际化 -可以在resources.yaml对应的 `x-bk-apigateway-resource` 的 `descriptionEn` 指定英文描述 + +#### 1.4.3 资源描述国际化 + +可以在 resources.yaml 对应的 `x-bk-apigateway-resource` 的 `descriptionEn` 指定英文描述 + ```yaml x-bk-apigateway-resource: isPublic: false @@ -345,14 +357,14 @@ authConfig: userVerifiedRequired: false resourcePermissionRequired: false descriptionEn: anything - ``` -## 同步方案 -### 方案一:直接使用 Django Command 同步 +## 2. 同步方案 + +### 2.1 方案一:直接使用 Django Command 同步 此方案适用于 Django 项目,具体请参考 [sync-apigateway-with-django.md](./sync-apigateway-with-django.md) -### 方案二:通过镜像方式同步 +### 2.2 方案二:通过镜像方式同步 此方案适用于非 Django 项目,具体请参考 [sync-apigateway-with-docker.md](./sync-apigateway-with-docker.md) diff --git a/sdks/apigw-manager/pyproject.toml b/sdks/apigw-manager/pyproject.toml index 8a7f651..0b1bd6f 100644 --- a/sdks/apigw-manager/pyproject.toml +++ b/sdks/apigw-manager/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "apigw-manager" -version = "3.1.1" +version = "3.1.2" description = "The SDK for managing blueking gateway resource." readme = "README.md" authors = ["blueking "] diff --git a/sdks/apigw-manager/src/apigw_manager/drf/management/commands/data/definition.yaml b/sdks/apigw-manager/src/apigw_manager/drf/management/commands/data/definition.yaml index aa30381..b24cab3 100644 --- a/sdks/apigw-manager/src/apigw_manager/drf/management/commands/data/definition.yaml +++ b/sdks/apigw-manager/src/apigw_manager/drf/management/commands/data/definition.yaml @@ -21,28 +21,26 @@ apigateway: {% endif %} stages: - - name: "{{ settings.BK_APIGW_DEFAULT_STAGE_NAME }}" - description: "{{ settings.BK_APIGW_DEFAULT_STAGE_DESCRIPTION }}" - description_en: "{{ settings.BK_APIGW_DEFAULT_STAGE_DESCRIPTION_EN }}" - {% if settings.BK_APIGW_DEFAULT_STAGE_BACKEND_SUBPATH %} + - name: "{{ settings.BK_APIGW_STAGE_NAME }}" + description: "{{ settings.BK_APIGW_STAGE_DESCRIPTION }}" + description_en: "{{ settings.BK_APIGW_STAGE_DESCRIPTION_EN }}" + {% if settings.BK_APIGW_STAGE_BACKEND_SUBPATH %} vars: - api_sub_path: {{ settings.BK_APIGW_DEFAULT_STAGE_BACKEND_SUBPATH }} + api_sub_path: {{ settings.BK_APIGW_STAGE_BACKEND_SUBPATH }} {% else %} vars: {} {% endif %} backends: - name: "default" config: - timeout: 60 + timeout: {{ settings.BK_APIGW_STAG_BACKEND_TIMEOUT }} loadbalance: "roundrobin" hosts: - # 网关调用后端服务的默认域名或IP,不包含Path,比如:http://api.example.com - - host: "{{ settings.BK_APIGW_DEFAULT_STAGE_BACKEND_HOST }}" + - host: "{{ settings.BK_APIGW_STAGE_BACKEND_HOST }}" weight: 100 - # TODO: support plugin_configs - {% if settings.BK_APIGW_DEFAULT_STAGE_PLUGIN_CONFIGS %} + {% if settings.BK_APIGW_STAGE_PLUGIN_CONFIGS %} plugin_configs: - {% for plugin_config in settings.BK_APIGW_DEFAULT_STAGE_PLUGIN_CONFIGS %} + {% for plugin_config in settings.BK_APIGW_STAGE_PLUGIN_CONFIGS %} - type: {{ plugin_config.type }} yaml: |- {{ plugin_config.yaml }} @@ -58,4 +56,4 @@ grant_permissions: {% endif %} related_apps: - - "{{ settings.BK_APP_CODE }}" \ No newline at end of file + - "{{ settings.BK_APP_CODE }}" diff --git a/sdks/apigw-manager/src/apigw_manager/drf/management/commands/generate_resources_yaml.py b/sdks/apigw-manager/src/apigw_manager/drf/management/commands/generate_resources_yaml.py index c0cd8aa..35425fc 100644 --- a/sdks/apigw-manager/src/apigw_manager/drf/management/commands/generate_resources_yaml.py +++ b/sdks/apigw-manager/src/apigw_manager/drf/management/commands/generate_resources_yaml.py @@ -15,6 +15,7 @@ 2. call this command with tag, e.g. `python manage.py generate_resource_yaml.py --tag=foo --tag=bar` """ +from pathlib import Path from typing import List from django.conf import settings @@ -23,7 +24,6 @@ from drf_spectacular.renderers import OpenApiYamlRenderer from drf_spectacular.settings import spectacular_settings from drf_spectacular.validation import validate_schema -from pathlib import Path def post_process_only_keep_the_apis_with_specified_tags(tags: List) -> callable: @@ -51,7 +51,7 @@ def post_process_inject_method_and_path(result, generator, request, public): paths = result.get("paths", None) if not paths: return result - sub_path = settings.BK_APIGW_DEFAULT_STAGE_BACKEND_SUBPATH + sub_path = settings.BK_APIGW_STAGE_BACKEND_SUBPATH for uri, methods in paths.items(): for method, info in methods.items(): @@ -84,7 +84,7 @@ def handle(self, *args, **kwargs): else: self.stdout.write("no argument --tag, will use all apis under the project") - self.stdout.write(f"process the project sub_path={settings.BK_APIGW_DEFAULT_STAGE_BACKEND_SUBPATH}") + self.stdout.write(f"process the project sub_path={settings.BK_APIGW_STAGE_BACKEND_SUBPATH}") spectacular_settings.POSTPROCESSING_HOOKS.append(post_process_inject_method_and_path) generator = spectacular_settings.DEFAULT_GENERATOR_CLASS() diff --git a/sdks/apigw-manager/src/apigw_manager/drf/management/commands/sync_drf_apigateway.py b/sdks/apigw-manager/src/apigw_manager/drf/management/commands/sync_drf_apigateway.py index cdeebe1..415fc26 100644 --- a/sdks/apigw-manager/src/apigw_manager/drf/management/commands/sync_drf_apigateway.py +++ b/sdks/apigw-manager/src/apigw_manager/drf/management/commands/sync_drf_apigateway.py @@ -24,6 +24,7 @@ """ from pathlib import Path + from django.conf import settings from django.core.management import call_command from django.core.management.base import BaseCommand @@ -56,16 +57,17 @@ def handle(self, *args, **kwargs): f"--gateway-name={gateway_name}", "--delete", f"--file={resources_file_path}", + f"--doc_language={settings.BK_APIGW_RELEASE_DOC_LANGUAGE}", ) self.stdout.write( - f"call create_version_and_release_apigw with definition: {definition_file_path}, stage: {settings.BK_APIGW_DEFAULT_STAGE_NAME}" + f"call create_version_and_release_apigw with definition: {definition_file_path}, stage: {settings.BK_APIGW_STAGE_NAME}" ) call_command( "create_version_and_release_apigw", f"--gateway-name={gateway_name}", f"--file={definition_file_path}", - f"--stage={settings.BK_APIGW_DEFAULT_STAGE_NAME}", + f"--stage={settings.BK_APIGW_STAGE_NAME}", ) self.stdout.write(f"call grant_apigw_permissions with definition: {definition_file_path}") diff --git a/sdks/apigw-manager/tests/apigw_manager/drf/management/commands/test_generate_resources_yaml.py b/sdks/apigw-manager/tests/apigw_manager/drf/management/commands/test_generate_resources_yaml.py index 133511f..5eeee29 100644 --- a/sdks/apigw-manager/tests/apigw_manager/drf/management/commands/test_generate_resources_yaml.py +++ b/sdks/apigw-manager/tests/apigw_manager/drf/management/commands/test_generate_resources_yaml.py @@ -18,12 +18,12 @@ @pytest.fixture() def django_settings_subpath_empty(settings): - settings.BK_APIGW_DEFAULT_STAGE_BACKEND_SUBPATH = "" + settings.BK_APIGW_STAGE_BACKEND_SUBPATH = "" @pytest.fixture() def django_settings_subpath(settings): - settings.BK_APIGW_DEFAULT_STAGE_BACKEND_SUBPATH = "/mock" + settings.BK_APIGW_STAGE_BACKEND_SUBPATH = "/mock" class TestPostProcess: