-
Notifications
You must be signed in to change notification settings - Fork 11
feat: 支持本地 WASM 文件构建 #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -173,3 +173,5 @@ cython_debug/ | |
| # PyPI configuration file | ||
| .pypirc | ||
| .idea | ||
| /plugins/* | ||
| !/plugins/.gitkeep | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,8 +2,9 @@ | |
| ARG PYTHON_IMAGE=higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/python:3.11-alpine | ||
| ARG NGINX_IMAGE=higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/nginx:alpine | ||
| ARG ALPINE_MIRROR="" | ||
| ARG USE_LOCAL_PLUGINS=false | ||
|
|
||
| FROM $PYTHON_IMAGE AS builder | ||
| FROM $PYTHON_IMAGE AS builder-base | ||
|
|
||
| # 配置 Alpine 镜像源(可选,本地构建时可指定国内镜像加速) | ||
| ARG ALPINE_MIRROR | ||
|
|
@@ -26,14 +27,28 @@ RUN set -eux; \ | |
| && rm -rf /tmp/oras.tar.gz oras \ | ||
| && oras version | ||
|
|
||
| # 创建工作目录 | ||
| WORKDIR /workspace | ||
|
|
||
| # 复制脚本 | ||
| # 复制脚本和公共文件 | ||
| COPY pull_plugins.py plugins.properties ./ | ||
|
|
||
| # 执行构建操作 | ||
| RUN python3 pull_plugins.py --download-v2 | ||
| FROM builder-base AS local-true | ||
| # 启用本地模式:复制本地插件源 | ||
| COPY plugins/ ./plugins/ | ||
|
|
||
| FROM builder-base AS local-false | ||
| # 不启用本地模式:创建空的 plugins 目录 | ||
| RUN mkdir -p plugins | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 本 PR 引入 (cleanup): 失败场景: 多出一个空 RUN 层(每层几 MB、增加 cache key 一个 hash),reader 必须跳到脚本验证 mkdir 是否必需。删除后 修复建议: 直接删除该 RUN 行。 |
||
|
|
||
| FROM local-${USE_LOCAL_PLUGINS:-false} AS builder | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 本 PR 引入: 失败场景: 用户按常见约定传 修复建议: 在 builder 阶段开头加一次规范化(例如 |
||
|
|
||
| # 根据 USE_LOCAL_PLUGINS 决定是否启用本地 WASM 文件覆盖 | ||
| ARG USE_LOCAL_PLUGINS | ||
| RUN if [ "$USE_LOCAL_PLUGINS" = "true" ]; then \ | ||
| python3 pull_plugins.py --download-v2 --use-local; \ | ||
| else \ | ||
| python3 pull_plugins.py --download-v2; \ | ||
| fi | ||
|
|
||
| # 运行阶段:最终镜像 | ||
| FROM $NGINX_IMAGE | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # 此占位文件确保 plugins/ 目录被 git 跟踪,从而使 Dockerfile 的 | ||
| # "COPY plugins/ ./plugins/" 在全新克隆后仍有源目录可用。 | ||
| # 本地/自定义插件以 plugins/<插件名>/<版本>/plugin.wasm 形式放入此目录; | ||
| # 下载产物与 metadata.txt 由构建生成,不纳入版本控制(见 .gitignore)。 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -93,61 +93,46 @@ def generate_metadata(plugin_dir, plugin_name): | |
| except Exception as e: | ||
| print(f"生成元数据失败: {e}") | ||
|
|
||
| def main(): | ||
| parser = argparse.ArgumentParser(description='处理插件配置文件') | ||
| parser.add_argument('properties_path', nargs='?', default=None, | ||
| help='properties文件路径(默认:脚本所在目录下的plugins.properties)') | ||
| parser.add_argument('--download-v2', action='store_true', | ||
| help='是否下载 2.0.0 版本插件') | ||
| args = parser.parse_args() | ||
|
|
||
| # 用户未提供路径时,使用默认逻辑 | ||
| if args.properties_path is None: | ||
| script_dir = os.path.dirname(os.path.abspath(__file__)) | ||
| default_path = os.path.join(script_dir, 'plugins.properties') | ||
|
|
||
| args.properties_path = default_path | ||
|
|
||
| base_path = os.path.dirname(args.properties_path) | ||
| properties = read_properties(args.properties_path) | ||
|
|
||
| if not properties: | ||
| print("未找到有效的插件配置") | ||
| return | ||
|
|
||
| failed_plugins = [] | ||
|
|
||
| for plugin_name, plugin_url in properties.items(): | ||
| print(f"\n正在处理插件: {plugin_name}") | ||
| # 处理原始版本(1.0.0) | ||
| success = process_plugin(base_path, plugin_name, plugin_url, "1.0.0") | ||
| if not success: | ||
| failed_plugins.append(f"{plugin_name}:1.0.0") | ||
|
|
||
| # 如果指定了 --download-v2 参数,则额外处理 2.0.0 版本 | ||
| if args.download_v2: | ||
| v2_url = plugin_url.replace(":1.0.0", ":2.0.0") | ||
| print(f"\n正在处理插件 {plugin_name} 的 2.0.0 版本") | ||
| success = process_plugin(base_path, plugin_name, v2_url, "2.0.0") | ||
| if not success: | ||
| failed_plugins.append(f"{plugin_name}:2.0.0") | ||
|
|
||
| if failed_plugins: | ||
| print("\n以下插件未成功处理:") | ||
| for plugin in failed_plugins: | ||
| print(f"- {plugin}") | ||
| sys.exit(1) | ||
|
|
||
| def process_plugin(base_path, plugin_name, plugin_url, version): | ||
| def generate_all_metadata(base_path): | ||
| """ | ||
| 统一生成 metadata.txt:扫描 plugins/<name>/<version>/plugin.wasm, | ||
| 覆盖 properties 中下载/跳过的插件、本地预置插件,以及未在 properties | ||
| 中定义的自定义插件。临时目录(<name>_<version>_temp)与失败目录 | ||
| 因不匹配该路径模板,不会被遍历。 | ||
| """ | ||
| plugins_base_path = os.path.join(base_path, 'plugins') | ||
| if not os.path.isdir(plugins_base_path): | ||
| return 0 | ||
| count = 0 | ||
| for plugin_name in sorted(os.listdir(plugins_base_path)): | ||
| name_dir = os.path.join(plugins_base_path, plugin_name) | ||
| if not os.path.isdir(name_dir): | ||
| continue | ||
| for version in sorted(os.listdir(name_dir)): | ||
| plugin_dir = os.path.join(name_dir, version) | ||
| if os.path.isfile(os.path.join(plugin_dir, 'plugin.wasm')): | ||
| generate_metadata(plugin_dir, plugin_name) | ||
| count += 1 | ||
| print(f"共为 {count} 个插件目录生成 metadata.txt") | ||
| return count | ||
|
|
||
| def process_plugin(base_path, plugin_name, plugin_url, version, use_local=False): | ||
| """ | ||
| 处理单个插件下载和信息获取 | ||
| 处理单个 OCI 插件:把 plugin.wasm 放到 plugins/<name>/<version>/。 | ||
| 本地已预置(通过 Dockerfile 的 COPY plugins/)则跳过下载。 | ||
| metadata.txt 不在此处生成,由 generate_all_metadata() 统一处理。 | ||
| """ | ||
| plugins_base_path = os.path.join(base_path, 'plugins') | ||
| os.makedirs(plugins_base_path, exist_ok=True) | ||
|
|
||
| plugin_dir = os.path.join(plugins_base_path, plugin_name, version) | ||
| os.makedirs(plugin_dir, exist_ok=True) | ||
|
|
||
| # 只有 use_local=True 时才检查本地已预置的 plugin.wasm:跳过下载 | ||
| if use_local and os.path.isfile(os.path.join(plugin_dir, 'plugin.wasm')): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 本 PR 引入: 失败场景: 用户把截断的 修复建议: 在 line 132 之后增加 |
||
| print(f"{plugin_name} ({version}) 检测到本地副本,跳过下载") | ||
| return True | ||
|
|
||
| temp_download_dir = os.path.join(plugins_base_path, f"{plugin_name}_{version}_temp") | ||
| os.makedirs(temp_download_dir, exist_ok=True) | ||
|
|
||
|
|
@@ -191,13 +176,59 @@ def process_plugin(base_path, plugin_name, plugin_url, version): | |
| finally: | ||
| shutil.rmtree(temp_download_dir, ignore_errors=True) | ||
|
|
||
| if wasm_found: | ||
| generate_metadata(plugin_dir, plugin_name) | ||
| else: | ||
| if not wasm_found: | ||
| print(f"{plugin_name} ({version}) 未找到 .wasm 文件") | ||
| shutil.rmtree(plugin_dir, ignore_errors=True) | ||
|
|
||
| return wasm_found | ||
|
|
||
| def main(): | ||
| parser = argparse.ArgumentParser(description='处理插件配置文件') | ||
| parser.add_argument('properties_path', nargs='?', default=None, | ||
| help='properties文件路径(默认:脚本所在目录下的plugins.properties)') | ||
| parser.add_argument('--download-v2', action='store_true', | ||
| help='是否下载 2.0.0 版本插件') | ||
| parser.add_argument('--use-local', action='store_true', | ||
| help='启用本地 WASM 文件:plugins/<name>/<version>/plugin.wasm 存在时跳过 OCI 下载') | ||
| args = parser.parse_args() | ||
|
|
||
| # 用户未提供路径时,使用默认逻辑 | ||
| if args.properties_path is None: | ||
| script_dir = os.path.dirname(os.path.abspath(__file__)) | ||
| args.properties_path = os.path.join(script_dir, 'plugins.properties') | ||
|
|
||
| base_path = os.path.dirname(args.properties_path) | ||
| properties = read_properties(args.properties_path) | ||
|
|
||
| if not properties: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 本 PR 引入: 当 失败场景: 开发者用空 plugins.properties + 修复建议: 把 |
||
| print("未找到有效的插件配置") | ||
| return | ||
|
|
||
| failed_plugins = [] | ||
|
|
||
| for plugin_name, plugin_url in properties.items(): | ||
| print(f"\n正在处理插件: {plugin_name}") | ||
| # 处理原始版本(1.0.0) | ||
| success = process_plugin(base_path, plugin_name, plugin_url, "1.0.0", use_local=args.use_local) | ||
| if not success: | ||
| failed_plugins.append(f"{plugin_name}:1.0.0") | ||
|
|
||
| # 如果指定了 --download-v2 参数,则额外处理 2.0.0 版本 | ||
| if args.download_v2: | ||
| v2_url = plugin_url.replace(":1.0.0", ":2.0.0") | ||
| print(f"\n正在处理插件 {plugin_name} 的 2.0.0 版本") | ||
| success = process_plugin(base_path, plugin_name, v2_url, "2.0.0", use_local=args.use_local) | ||
| if not success: | ||
| failed_plugins.append(f"{plugin_name}:2.0.0") | ||
|
|
||
| # 统一生成 metadata.txt:覆盖下载的、本地预置的、以及未在 properties 中定义的自定义插件 | ||
| generate_all_metadata(base_path) | ||
|
|
||
| if failed_plugins: | ||
| print("\n以下插件未成功处理:") | ||
| for plugin in failed_plugins: | ||
| print(f"- {plugin}") | ||
| sys.exit(1) | ||
|
|
||
| if __name__ == '__main__': | ||
| main() | ||
| main() | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
本 PR 引入:
local-true阶段COPY plugins/ ./plugins/把本 PR 新增的plugins/.gitkeep拷进 builder 镜像,最终经 Dockerfile:57COPY --from=builder /workspace/plugins /usr/share/nginx/html/plugins泄漏到 nginx 运行时镜像;nginx.confautoindex on+root /usr/share/nginx/html使其可被 HTTP GET 拉取。失败场景:
docker build --build-arg USE_LOCAL_PLUGINS=true后,curl http://host:8080/plugins/.gitkeep返回 200 OK 与 ~331 字节中文注释,泄露构建使用了本地覆盖模式。仅 USE_LOCAL_PLUGINS=true 受影响。修复建议: 在 builder 阶段末尾加
RUN rm -f /workspace/plugins/.gitkeep(或find /workspace/plugins -name .gitkeep -delete);或在COPY plugins/ ./plugins/之后立即清理。