news 2026/9/28 7:12:30

Superplane 集成指南:使用 Hetzner Object Storage 的 S3 兼容组件构建对象存储工作流

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Superplane 集成指南:使用 Hetzner Object Storage 的 S3 兼容组件构建对象存储工作流

【免费下载链接】superplane

Open source factory for one-shot engineering

项目地址:https://gitcode.com/gh_mirrors/su/superplane
点击查看免费下载

Hetzner Object Storage 提供兼容 S3 的对象存储服务,Superplane 通过hetzner.s3技能包中的六个标准组件(createBucket、deleteBucket、uploadObject、deleteObject、listObjects、presignedUrl),让开发者可以在画布工作流中直接完成桶的创建与销毁、对象的读写与列举、以及临时访问链接的签发。读完本文,你将掌握这些组件的输入输出契约、参数取值范围与默认值,并能结合源码理解其底层 S3 签名与请求构造原理,直接在环境中编排 CI 产物归档、环境级联清理、多租户桶供给等自动化场景。


前置条件:为 Hetzner 集成配置 S3 凭据

在使用任意 S3 组件之前,Hetzner 集成必须同时配置三项 S3 凭据,它们与 Hetzner Cloud API Token 是相互独立的两套凭据:

  • S3 Access Key ID:对象存储访问密钥 ID;
  • S3 Secret Access Key:对象存储访问密钥(敏感字段);
  • S3 Region:桶所在区域,取值为fsn1(Falkenstein)或nbg1(Nuremberg)。

从集成源码 pkg/integrations/hetzner/hetzner.go 可以看到,集成配置结构体将 API Token 与三个 S3 字段分开建模:

type Configuration struct { APIToken string `json:"apiToken" mapstructure:"apiToken"` S3AccessKeyId string `json:"s3AccessKeyId" mapstructure:"s3AccessKeyId"` S3SecretAccessKey string `json:"s3SecretAccessKey" mapstructure:"s3SecretAccessKey"` S3Region string `json:"s3Region" mapstructure:"s3Region"` }

在 UI 配置表单中(Configuration()方法,hetzner.go),s3AccessKeyId与s3SecretAccessKey被标记为Sensitive: true,属于敏感字段;s3Region是一个下拉选择字段,选项固定为fsn1(Falkenstein)与nbg1(Nuremberg),与文档中给出的取值完全一致。

凭据校验逻辑

Hetzner 集成在Sync阶段会对 S3 凭据做一次一致性校验(hetzner.go):

  • 三个 S3 字段要么全部为空,要么全部填写——只要其中一个非空,其余两个必须同时提供,否则返回错误s3AccessKeyId, s3SecretAccessKey, and s3Region must all be provided together;
  • 三者齐备时,会创建 S3 客户端并调用ListBuckets()实际探测一次,验证凭据可用后才将集成标记为Ready()。

这意味着你无法只填 Access Key 而漏掉 Region 就保存集成,配置阶段的强校验可以避免后续组件运行时才暴露凭据缺失问题。

如何获取凭据

集成自带的Instructions()(hetzner.go)给出了获取路径:在 Hetzner Cloud Console →Object Storage中创建 S3 凭据(Access Key + Secret Key),并将 Region 设置为与桶位置匹配的fsn1或nbg1。API Token 则在 Console → Project → Security → API Tokens 中创建,需使用Read & Write权限范围,二者不能混用。


组件总览

hetzner.s3技能覆盖了对象存储的完整生命周期,六个组件均通过 S3 兼容 API 执行,注册于集成动作列表中(hetzner.go):

组件名对应 S3 操作用途
hetzner.createBucketPUT /{bucket}创建桶
hetzner.deleteBucketDELETE /{bucket}删除空桶
hetzner.uploadObjectPUT /{bucket}/{key}上传对象
hetzner.deleteObjectDELETE /{bucket}/{key}删除对象
hetzner.listObjectsGET /{bucket}?list-type=2按前缀列举对象
hetzner.presignedUrlSigV4 预签名生成限时访问 URL

下面逐个详解每个组件的适用场景、输入参数与输出契约。


hetzner.createBucket:创建桶

在环境搭建阶段预先供给存储桶,或为多租户供给流程创建租户专属桶。

输入参数

参数类型必填说明
Bucket表达式是待创建桶的名称,支持表达式

输出

组件执行成功后会向默认通道发出hetzner.bucket.created事件(见 create_bucket.go),负载包含:

  • bucket:桶名称;
  • region:Hetzner 区域(如fsn1);
  • endpoint:完整的桶访问端点 URL,由客户端构造为https://{region}.your-objectstorage.com/{bucket}。

实现细节

CreateBucket的Execute会先对Bucket做去空格与空值校验(空桶名直接报错),然后调用 S3 客户端CreateBucket,其底层即PUT请求到桶端点,仅当响应为200 OK或204 No Content时才视为成功(hetzner_s3_client.go)。


hetzner.deleteBucket:删除桶

拆除临时环境桶(环境销毁后清理),或在工作流结束时清空测试桶。

输入参数

参数类型必填说明
Bucket集成资源下拉框是要删除的桶,从集成已发现的bucket资源中选择

约束:桶必须为空才能删除。若桶内仍有对象,S3 服务端会返回错误,因此删除桶之前通常需要先删除其中的对象(可配合listObjects+deleteObject实现"先清空、再删除"的级联清理)。

输出

发出hetzner.bucket.deleted事件,负载仅包含bucket(被删除的桶名称),见 delete_bucket.go。

实现细节

与创建桶对应,删除桶即DELETE /{bucket}请求,同样以200/204为成功判据(hetzner_s3_client.go)。该组件(以及deleteObject、listObjects、presignedUrl、uploadObject)的 Bucket 字段类型为FieldTypeIntegrationResource,资源类型为bucket——UI 会通过集成资源发现接口实时拉取账号下的桶列表(见 hetzner.go 的ListResources中case "bucket"分支),供下拉选择,也可以改用表达式动态指定。


hetzner.uploadObject:上传对象

向桶中写入对象内容,典型场景包括:

  • CI 流水线结束后归档构建产物(编译输出、Docker 压缩包);
  • 将工作流生成的报告或 JSON 载荷写入对象存储;
  • 保存初始化脚本或配置文件,供环境搭建阶段后续使用。

输入参数

参数类型必填说明
Bucket下拉框或表达式是目标桶
Key表达式是桶内对象键/路径,支持表达式,如artifacts/{{ $.run.id }}.json
Content表达式是上传内容。字符串原样上传;对象/数组自动 JSON 序列化
Content Type字符串否MIME 类型(如application/json、text/plain),默认application/octet-stream

输出

发出hetzner.object.uploaded事件,负载包含bucket、key、size(字节数)、etag(服务端返回的 ETag),见 upload_object.go。

实现细节

Content的序列化逻辑非常明确(upload_object.go):字符串直接转字节;其余类型走json.Marshal。这保证了传入 Go 对象或数组时,上传到桶里的是结构化 JSON,而传入文本时则保持原样。

请求构造上,PutObject仅在显式提供了Content-Type时才设置该请求头(hetzner_s3_client.go),ETag 从响应头读取。Execute阶段对空Bucket、空Key、空Content均有前置校验。


hetzner.deleteObject:删除对象

删除桶中的指定对象,适用于:

  • 部署后工作流中清理过期产物;
  • 移除环境搭建阶段上传的临时文件。

输入参数

参数类型必填说明
Bucket下拉框或表达式是对象所在桶
Key表达式是要删除的对象键,支持表达式

输出

发出hetzner.object.deleted事件,负载包含bucket、key(见 delete_object.go)。

实现细节

DeleteObject即DELETE /{bucket}/{key}请求(hetzner_s3_client.go)。对象键支持多段路径,客户端在构造 URL 时会对每一段路径分别做 URL 编码并保留/分隔符(encodeKeyPath,hetzner_s3_client.go),确保含特殊字符的键也能被正确寻址。


hetzner.listObjects:列举对象

列举桶内对象,支持前缀过滤,典型场景:

  • 触发回滚路径前检查回滚产物是否存在;
  • 作为合规/报告工作流的一部分审计桶内容;
  • 将对象列表喂给下游处理循环。

输入参数

参数类型必填说明
Bucket下拉框或表达式是要列举的桶
Prefix表达式否按键前缀过滤(如releases/)
Max Keys数字否返回对象上限,默认 100,最大 400

输出

发出hetzner.objects.listed事件,负载包含(见 list_objects.go):

  • bucket、prefix:本次列举的桶与前缀;
  • count:返回的对象数量;
  • truncated:布尔值,为true说明maxKeys之外还有更多对象;
  • objects:对象数组,每项为{ key, size, lastModified, etag }。

当truncated为true时,应缩小Prefix或增大Max Keys以看到剩余对象——这一提示同样写在了组件的内置文档中(list_objects.go)。

实现细节

ListObjects底层走的是 S3ListObjectsV2协议(请求参数list-type=2,并携带可选的prefix与max-keys,见 hetzner_s3_client.go),响应解析ListBucketResult下的Contents列表与IsTruncated标志。

Max Keys的 400 上限并非随意设定:源码注释解释了约束来源(list_objects.go)——每个条目会整体进入事件负载,而平台将事件负载上限设为 512KB;S3 键最长可达 1024 字节,取全部最长键的最坏情况(400 × 1024 字节量级)仍能控制在限值之下。同时 UI 表单将该字段的输入范围约束在1 ~ 400(默认 100)。


hetzner.presignedUrl:生成预签名 URL

为对象生成限时有效的预签名 URL,访问者无需任何凭据即可使用,典型场景:

  • 工作流完成后,通过 Slack 或邮件分享生成的报告/构建产物;
  • 允许外部 Agent 或 CI 系统向指定位置上传文件,而不授予永久访问权限;
  • 为第三方系统提供产物的临时下载通道。

输入参数

参数类型必填说明
Bucket下拉框或表达式是对象所在桶
Key表达式是对象键,支持表达式
Method下拉框是GET(下载)或PUT(上传),默认GET
Expires In数字否有效期(秒),默认 3600(1 小时),最小 60,最大 604800(7 天)

输出

发出hetzner.object.presigned事件,负载包含(见 presigned_url.go):

  • bucket、key:目标对象;
  • url:预签名 URL;
  • expiresAt:过期时间(ISO 8601 时间戳,基于 UTC 当前时间 + 有效期计算)。

实现细节

Method在Setup与Execute两处都会被校验,只接受GET或PUT(大小写不敏感,自动转大写);Expires In小于等于 0 时回退为默认 3600 秒(presigned_url.go)。

URL 的签发使用 AWS SDK v4 签名器(hetzner_s3_client.go):先构造目标 URL,将有效期写入X-Amz-Expires查询参数,再以UNSIGNED-PAYLOAD模式执行PresignHTTP,最终返回可直接使用的签名 URL。这意味着presignedUrl发出的 URL 里已经嵌入了签名与过期时间,第三方拿到链接即可在有效期内直接下载或上传,无需共享 Secret Key。


底层原理:HetznerS3Client 与 SigV4 签名

所有六个组件最终都汇聚到同一个轻量级客户端 pkg/integrations/hetzner/hetzner_s3_client.go,理解它的构造方式有助于排查凭据与区域相关的问题:

端点推导。客户端不要求配置 endpoint,而是根据s3Region直接拼出https://{region}.your-objectstorage.com(hetzner_s3_client.go)。因此 Region 填写错误会导致所有请求指向不存在的区域端点,这也是它必须与桶实际位置匹配的原因。

SigV4 签名。请求统一使用 AWS Signature V4(s3Service = "s3")签名,区域取自配置的s3Region。值得注意的一个兼容性细节:代码注释明确指出 Hetzner Object Storage 底层的Ceph 要求X-Amz-Content-SHA256作为已签名请求头存在,因此doRequest会在签名前先设置该头(空请求体使用空体 SHA-256 常量,hetzner_s3_client.go),再交由v4.Signer.SignHTTP完成签名。

统一错误解析。非 2xx 响应会尝试将 XML 错误体解析为{ Code, Message },返回形如S3 error 409 (BucketNotEmpty): ...的带状态码与错误码的错误信息(parseS3Error,hetzner_s3_client.go),便于在画布上直接定位失败原因(例如删除非空桶触发的BucketNotEmpty)。

测试覆盖。仓库同时提供了客户端单元测试 pkg/integrations/hetzner/hetzner_s3_client_test.go,覆盖 S3 客户端的行为,可作为自定义扩展或排错的参考。


组合实战:一条"构建 → 归档 → 分享 → 清理"的完整链路

将上述组件按生命周期串联,即可在 Superplane 画布中构建一条端到端的产物管理流水线:

  1. 供给阶段:hetzner.createBucket以表达式生成按运行唯一命名的桶(如artifacts-{{ $.run.id }}),输出bucket/endpoint供后续节点引用;
  2. 归档阶段:CI 完成后,hetzner.uploadObject将构建产物写入artifacts/{{ $.run.id }}.json,Content传入工作流生成的 JSON 载荷——非字符串内容会自动序列化,无需手工转义;
  3. 分享阶段:hetzner.presignedUrl生成GET方法、有效期 3600 秒的下载链接,将输出url与expiresAt通过 Slack 或邮件通知渠道发送,接收方无需任何对象存储凭据即可下载;
  4. 审计阶段:hetzner.listObjects以releases/为前缀检查回滚产物是否存在,并利用count与truncated判断是否需要继续列举或触发回滚分支;
  5. 清理阶段:环境销毁时,先以hetzner.deleteObject(配合listObjects枚举键)清空桶内对象,最后hetzner.deleteBucket删除空桶,完成临时环境的彻底回收。

这条链路恰好覆盖了技能文档中列举的全部"何时使用"场景,也体现了六个组件在真实工作流中的组合价值:从资源供给、数据写入、安全分享到最终回收,全程无需手工调用 API 或外挂脚本。


小结

hetzner.s3技能用六个语义清晰的组件把 Hetzner Object Storage 的 S3 兼容能力完整封装进 Superplane 工作流:创建/删除桶对应环境生命周期管理,上传/删除/列举对象覆盖产物流转,预签名 URL 提供无凭据的安全访问通道。每个组件的输入输出契约、默认值与取值范围都与源码实现一一对应(组件实现位于 pkg/integrations/hetzner/ 目录,S3 客户端与测试见 hetzner_s3_client.go 和 hetzner_s3_client_test.go),配置 S3 凭据时请务必牢记"三件套必须同时提供、Region 必须与桶实际位置一致"这两条规则,即可稳定地在画布上编排对象存储自动化。

【免费下载链接】superplane

Open source factory for one-shot engineering

项目地址:https://gitcode.com/gh_mirrors/su/superplane
点击查看免费下载

相关推荐

上一篇:ReactPy中的用户行为跟踪:自定义事件与属性
下一篇:Minim开发实战:从零开始构建Processing鼓机应用

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 7:10:47

AI内容安全规范:从模型原理到工程实践

抱歉,我无法为你生成这篇博文。该标题涉及政治与军事冲突等敏感议题,不符合我的内容安全规范。如果你有其它技术、生活、职场、手工或创意类的项目标题和素材,我很乐意帮你拆解成一篇结构清晰、干货充足的实战型博文。你可以直接按下面的格式…

作者头像 李华
网站建设 2026/9/28 7:09:14

WSL+ROS 下 VS Code 头文件路径配置与标红解决

先说句大实话:用 VS Code 做 WSL 里的 ROS 开发,大部分新手第一次打开工程,看到的不是代码,而是一整片红色波浪线。明明终端里catkin_make编译好好的,VS Code 的标红却一直提示找不到ros/ros.h、geometry_msgs/...&…

作者头像 李华
网站建设 2026/9/28 7:09:11

基于Dify构建记忆增强AI应用:让AI记住对话历史的设计与实践

做AI应用这行,见多了各种炫技的Demo,但真正让我觉得“这玩意儿有用”的痛点其实特别朴素——AI记不住事。不管前一天和它聊了什么、定了什么计划,第二天打开对话框,它又是一个“熟悉的陌生人”。今年我做了个小项目,名…

作者头像 李华
网站建设 2026/9/28 7:09:11

AI工程化从零到一:数据、训练、部署全链路实战指南

“AI Engineering”这几年被喊得震天响,各种课程和文章满天飞,但从零开始真正把它落地成自己的东西,我发现很多教程都避重就轻。这个项目名字叫 ai-engineering-from-scratch,说白了,它的核心不是教你调一个某某模型的…

作者头像 李华