Harbor 端点(Endpoint)删除功能验证:Admin 删除注册表端点的完整测试与实现原理

发布时间:2026/9/10 8:39:16
Harbor 端点(Endpoint)删除功能验证:Admin 删除注册表端点的完整测试与实现原理 Harbor 端点Endpoint删除功能验证Admin 删除注册表端点的完整测试与实现原理【免费下载链接】harborAn open source trusted cloud native registry project that stores, signs, and scans content.项目地址: https://gitcode.com/GitHub_Trending/ha/harbor导读本文围绕 Harbor 复制Replication模块中端点Endpoint删除这一核心管理操作以官方测试用例 7-14-Endpoints-endpoints-delete.md 为骨架展开先梳理该用例的测试目的、环境要求与验证步骤再深入 Harbor 源码揭示被复制规则引用的端点无法删除、未被引用的端点可以删除这一预期结果背后的引用检查机制、REST API 调用链与数据库设计。读完本文你将掌握 Harbor 中注册表端点Registries删除功能的完整验证方法、底层实现原理以及如何通过 UI 与 API 两种方式安全地清理不再使用的端点。一、测试用例定位端点删除在 Harbor 复制体系中的角色在 Harbor 的复制Replication功能中端点Endpoint即注册表Registry是复制的源Source或目标Destination对象。管理员在Administration - Registries页面维护这些端点的连接信息类型、URL、访问凭据、是否跳过证书校验等复制规则Replication Policy则在这些端点之间搬运制品镜像、Chart 等。测试用例 7-14 归属于Group7-Replication测试组其核心目的是To verify admin user can delete an endpoint.验证管理员用户可以删除端点。这条用例表面上只验证删除这一个动作但它真正的技术含量在于验证 Harbor 对端点删除的保护性约束端点可能正被复制规则或代理缓存项目引用此时删除必须被拒绝以防止复制链路出现悬空引用。测试环境要求用例明确列出了两条环境前提必须有一个正在运行且可访问的 Harbor 实例one Harbor instance is running and available至少存在一个端点At least one endpoint should exist。这意味着该用例属于状态依赖型验证要完整跑通可删除/不可删除两个分支环境中至少需要一个被规则引用的端点和一个未被引用的端点。二、验证步骤详解如何正确执行端点删除测试按照用例原文测试步骤如下以管理员admin用户登录 Harbor UI在Administration - Registries页面删除一个**正被复制规则使用in use by a rule**的端点在Administration - Registries页面删除一个**未被任何规则使用not in use by a rule**的端点。预期结果Expected Outcome步骤操作对象预期结果步骤 2被复制规则引用的端点删除失败endpoint can not be deleted步骤 3未被复制规则引用的端点删除成功endpoint can be deleted实操细节补充在真实环境中执行该验证时可按如下方式构造前置条件登录 Harbor UI进入Administration - Registries先创建一个远端端点例如类型选择Docker Hub或Harbor填写 URL 与访问凭据到Administration - Replications创建一条复制规则将刚才创建的端点作为源或目标端点此时该端点即成为in use by a rule的状态回到Administration - Registries页面分别对被引用端点和未被引用端点点击删除按钮对被引用端点页面应提示删除失败并给出相应错误信息如被复制策略引用无法删除对未被引用端点删除成功列表刷新后该端点消失。三、预期结果背后的源码原理引用检查三重关卡为什么被规则引用的端点删不掉这不是 UI 层的简单提示而是由后端控制器在删除前执行的引用完整性检查强制保证的。下面逐层拆解。3.1 请求入口REST API 的权限与路由端点删除对应的 HTTP 接口为DELETE /api/v2.0/registries/{id}该接口在 Swagger 定义中见 api/v2.0/swagger.yaml声明了操作摘要Delete the specific registry路径参数id为 int64 类型的 Registry ID并定义了如下响应码200删除成功401未认证403无权限404端点不存在412前置条件不满足Precondition Failed500服务器内部错误其中412正是端点被引用、无法删除时的语义化响应码。服务端实现位于 src/server/v2.0/handler/registry.gofunc (r *registryAPI) DeleteRegistry(ctx context.Context, params operation.DeleteRegistryParams) middleware.Responder { if err : r.RequireSystemAccess(ctx, rbac.ActionDelete, rbac.ResourceRegistry); err ! nil { return r.SendError(ctx, err) } if err : r.ctl.Delete(ctx, params.ID); err ! nil { return r.SendError(ctx, err) } return operation.NewDeleteRegistryOK() }可以看到DeleteRegistry处理函数首先通过RequireSystemAccess(ctx, rbac.ActionDelete, rbac.ResourceRegistry)做 RBAC 校验只有具备registry资源delete动作权限的系统级用户如 admin才能执行删除——这正是测试用例要求以 admin 登录的原因。权限通过后实际删除逻辑委托给控制器registry.Ctl.Delete。3.2 核心逻辑控制器层的前置条件检查删除的真正业务逻辑在 src/controller/registry/controller.go 的Delete方法中它依次做了三次引用计数检查func (c *controller) Delete(ctx context.Context, id int64) error { // referenced by replication policy as source registry count, err : c.repMgr.Count(ctx, q.Query{ Keywords: map[string]any{src_registry_id: id}, }) if err ! nil { return err } if count 0 { return errors.New(nil).WithCode(errors.PreconditionCode). WithMessagef(the registry %d is referenced by replication policies, cannot delete it, id) } // referenced by replication policy as destination registry count, err c.repMgr.Count(ctx, q.Query{ Keywords: map[string]any{dest_registry_id: id}, }) if err ! nil { return err } if count 0 { return errors.New(nil).WithCode(errors.PreconditionCode). WithMessagef(the registry %d is referenced by replication policies, cannot delete it, id) } // referenced by proxy cache project count, err c.proMgr.Count(ctx, q.Query{ Keywords: map[string]any{registry_id: id}, }) if err ! nil { return err } if count 0 { return errors.New(nil).WithCode(errors.PreconditionCode). WithMessagef(the registry %d is referenced by proxy cache project, cannot delete it, id) } return c.regMgr.Delete(ctx, id) }这三道检查分别对应三种引用关系作为复制规则的源端点统计replication_policy表中src_registry_id等于该端点 ID 的策略数作为复制规则的目标端点统计replication_policy表中dest_registry_id等于该端点 ID 的策略数作为代理缓存项目的远端源统计project表中registry_id等于该端点 ID 的项目数proxy cache 项目同样以端点为远端仓库。任一处计数大于 0都会返回errors.PreconditionCode对应 HTTP 412并附带明确错误消息最终regMgr.Delete不会被调用端点保留。只有三次计数全部为 0才会进入真正的删除动作。从源码结构可以推断这里采用的是检查后删除的非原子模式Check-then-Delete在极端并发场景下仍存在检查与删除之间的竞态窗口但在常规单管理员操作场景下足够可靠。3.3 数据层物理删除与幂等语义当引用检查全部通过后调用链继续向下管理器层 src/pkg/reg/manager.go 的Delete直接委托 DAODAO 层 src/pkg/reg/dao/dao.go 执行真正的 ORM 删除func (d *dao) Delete(ctx context.Context, id int64) error { ormer, err : orm.FromContext(ctx) if err ! nil { return err } n, err : ormer.Delete(Registry{ID: id}) if err ! nil { return err } if n 0 { return errors.NotFoundError(nil).WithMessagef(registry %d not found, id) } return nil }这里有一个值得注意的实现细节DAO 删除时用n 0判断删除影响行数若目标端点本就不存在会返回NotFoundError404。也就是说重复删除同一端点会得到 404 而非静默成功这与测试预期中的可删除分支在语义上是自洽的。3.4 单元测试佐证三种分支全部被覆盖控制器层 src/controller/registry/controller_test.go 的TestDelete用例完整覆盖了上述三个分支func (r *registryTestSuite) TestDelete() { // referenced by replication policy mock.OnAnything(r.repMgr, Count).Return(int64(1), nil) err : r.ctl.Delete(nil, 1) r.NotNil(err) r.SetupTest() // referenced by proxy cache project mock.OnAnything(r.repMgr, Count).Return(int64(0), nil) mock.OnAnything(r.proMgr, Count).Return(int64(1), nil) err r.ctl.Delete(nil, 1) r.NotNil(err) r.SetupTest() // pass mock.OnAnything(r.repMgr, Count).Return(int64(0), nil) mock.OnAnything(r.proMgr, Count).Return(int64(0), nil) mock.OnAnything(r.regMgr, Delete).Return(nil) err r.ctl.Delete(nil, 1) r.Nil(err) }第一段模拟repMgr.Count返回 1被复制策略引用→ 断言删除返回错误第二段模拟复制策略引用为 0、但代理缓存项目引用为 1 → 断言删除返回错误第三段两次引用计数均为 0 → 断言删除成功且regMgr.Delete被调用。这三个分支与 7-14 用例的预期结果一一对应属于典型的UI 测试用例 单元测试双轨验证设计。四、数据模型registry 表的演进与引用字段理解删除保护机制还需了解底层数据表结构。4.1 端点表由 replication_target 演进而来Harbor 的端点表registry是从早期版本的replication_target表升级而来的。迁移脚本 make/migrations/postgresql/0004_1.8.0_schema.up.sql 记录了这一演进ALTER TABLE replication_target RENAME TO registry; ALTER TABLE registry ALTER COLUMN url TYPE varchar(256); ALTER TABLE registry ADD COLUMN credential_type varchar(16); ALTER TABLE registry RENAME COLUMN username TO access_key; ALTER TABLE registry RENAME COLUMN password TO access_secret; ALTER TABLE registry ALTER COLUMN access_secret TYPE varchar(4096); ALTER TABLE registry ADD COLUMN type varchar(32); ALTER TABLE registry DROP COLUMN target_type; ALTER TABLE registry ADD COLUMN description text; ALTER TABLE registry ADD COLUMN health varchar(16); UPDATE registry SET typeharbor; UPDATE registry SET credential_typebasic;从这段迁移可见端点表的核心列url端点地址最长 256 字符、credential_type认证类型、access_key/access_secret访问凭据secret 最长 4096 字符、type端点类型、description、health健康状态。4.2 引用字段复制策略表与项目表同一迁移脚本将replication_policy表的target_id重命名为dest_registry_id并新增src_registry_id见 0004_1.8.0_schema.up.sqlALTER TABLE replication_policy ADD COLUMN src_registry_id int; ALTER TABLE replication_policy RENAME COLUMN target_id TO dest_registry_id; ALTER TABLE replication_policy ALTER COLUMN dest_registry_id DROP NOT NULL;对应的 ORM 模型定义在 src/pkg/replication/model/model.goSrcRegistryID int64 orm:column(src_registry_id) DestRegistryID int64 orm:column(dest_registry_id)而代理缓存项目对端点的引用字段registry_id则由迁移脚本 make/migrations/postgresql/0040_2.1.0_schema.up.sql 为project表添加ALTER TABLE project ADD COLUMN IF NOT EXISTS registry_id int;这三处外键式关联src_registry_id、dest_registry_id、registry_id正是控制器层三次引用计数检查的统计依据。虽然这些列在数据库中未显式声明外键约束但 Harbor 通过应用层的引用检查补足了数据完整性保障。4.3 端点模型与特殊端点 ID端点模型定义见 src/pkg/reg/model/registry.go包含 ID、Name、Description、Type、URL、Credential、Insecure、CACertificate、Status 等字段。需要注意的是Harbor 将本机 Harbor 实例视为一个特殊端点其 ID 固定为 0。管理器 src/pkg/reg/manager.go 的Get方法在id 0时直接返回本地注册表信息getLocalRegistry类型为harbor名称为Local。因此该端点不参与常规删除流程删除 API 对 ID 0 也不会命中普通数据行。五、通过 API 验证端点删除curl 实践UI 操作背后对应的就是 REST API。在已登录拿到会话或认证令牌的前提下可以等价地用命令行验证 7-14 用例的两个分支# 1. 列出所有端点获取目标端点 ID curl -sk -u admin:password \ https://harbor-host/api/v2.0/registries?page_size100 # 2. 删除被复制规则引用的端点预期返回 412 Precondition Failed curl -sk -u admin:password -X DELETE \ https://harbor-host/api/v2.0/registries/5 \ -w \nHTTP status: %{http_code}\n # 3. 删除未被任何规则引用的端点预期返回 200 OK curl -sk -u admin:password -X DELETE \ https://harbor-host/api/v2.0/registries/6 \ -w \nHTTP status: %{http_code}\n验证要点步骤 2 返回412响应体包含the registry N is referenced by replication policies, cannot delete it之类的错误消息具体措辞见控制器实现 controller.go步骤 3 返回200随后再次调用 List 接口GET /api/v2.0/registries确认该端点已从列表中消失若端点 ID 不存在则返回404由 DAO 层的NotFoundError产生。六、回归验证与常见问题6.1 如何把该用例固化为回归测试7-14 属于手工 UI 用例其自动化回归可分层实施单元层src/controller/registry/controller_test.go的TestDelete已覆盖三种分支go test ./src/controller/registry/...可直接运行API 层可参考 src/server/v2.0/handler/registry_test.go 中基于htesting.Suite的 handler 测试模式用 mock 控制器断言删除接口的权限校验、参数传递与响应码端到端层tests/apitests/python目录下提供了基于 Python 的 Harbor API 测试框架可将其中的 Registries 相关脚本扩展出先建规则再删端点、先删规则再删端点的完整流程。6.2 常见问题排查现象可能原因处理方式删除端点时提示被复制策略引用该端点被某条复制规则用作源或目标先到Administration - Replications删除或修改相关规则再删除端点删除端点时提示被代理缓存项目引用该端点被某个 proxy cache 项目用作远端源先删除/停用对应代理缓存项目再删除端点删除返回 404端点 ID 不存在或已被删除通过GET /api/v2.0/registries确认实际 ID删除返回 403当前账号非系统管理员或缺少registry:delete权限使用具备系统管理权限的账号如 admin操作结语7-14 用例虽然只有三条测试步骤但它精准地刻画了 Harbor 端点删除功能的两条核心行为被引用的端点受到保护、未被引用的端点可被清理。这一行为的实现贯穿 UIAdministration - Registries、REST APIDELETE /api/v2.0/registries/{id}、控制器三次引用计数检查与数据层registry表及复制策略、项目表中的引用列四个层次。理解这条调用链不仅能让运维人员安全地管理复制端点也为扩展 Harbor 复制功能或排查删除失败问题提供了清晰的源码级线索。【免费下载链接】harborAn open source trusted cloud native registry project that stores, signs, and scans content.项目地址: https://gitcode.com/GitHub_Trending/ha/harbor创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询