Harbor 全局标签管理实战:系统管理员创建、编辑与删除标签的完整流程与源码解析
2026/9/11 12:06:20 网站建设 项目流程

Harbor 全局标签管理实战:系统管理员创建、编辑与删除标签的完整流程与源码解析

【免费下载链接】harborAn open source trusted cloud native registry project that stores, signs, and scans content.项目地址: https://gitcode.com/GitHub_Trending/ha/harbor

本篇技术指南围绕 Harbor 测试用例 11-01:系统管理员管理全局级标签 展开,系统讲解系统管理员(System Admin)如何在 Harbor 中完成全局级别标签的创建(Create)、编辑(Update)与删除(Delete)全流程,并结合本仓库的 REST API 定义、Handler 实现、Manager/DAO 分层与数据库迁移脚本,从 UI 操作与源码原理两个层面讲透"标签"这一核心元数据能力。读完本文,你将掌握全局标签的完整 CRUD 操作、REST 调用方式及其底层数据模型与权限控制机制。

标签体系:Level 与 Scope 双维度理解

在深入测试步骤之前,先建立对 Harbor 标签体系的基本认知。从源码 src/pkg/label/model/model.go 可以看到,标签Label模型包含以下关键字段:

字段类型说明
IDint64标签唯一主键
Namestring标签名称,最长 128 字符,不能为空
Descriptionstring标签描述
Colorstring标签颜色,用于 UI 展示
Levelstring标签级别:s(系统级)或u(用户级)
Scopestring标签范围:g(全局)或p(项目)
ProjectIDint64标签所属项目 ID,全局标签为 0
CreationTime/UpdateTimetime.Time创建 / 更新时间,由 ORM 自动维护
Deletedbool软删除标记

其中LevelScope的取值常量定义在 src/common/const.go:

  • LabelLevelSystem = "s":系统级标签,由 Harbor 内部逻辑使用;
  • LabelLevelUser = "u":用户级标签,即普通用户通过 UI/API 创建的标签;
  • LabelScopeGlobal = "g":全局标签,对所有项目可见,仅系统管理员可管理;
  • LabelScopeProject = "p":项目标签,隶属于某个具体项目,由项目成员按权限管理。

本文讨论的"全局级别标签"即Scope = "g"Level = "u"的用户级全局标签,它与项目标签的本质区别在于:全局标签不绑定任何项目(ProjectID恒为 0),创建后全 Harbor 实例内所有项目均可引用。

模型上的Valid()校验方法(见 model.go)强制约束:名称非空且不超过 128 字符;Scope只能是gp;当Scope = "p"ProjectID必须大于 0——从侧面印证了全局标签ProjectID必须为 0 这一设计。

测试用例 11-01 的验证目标

测试用例 11-01-system-admin-manage-global-level-labels.md 属于 Harbor 测试用例集中的 Group11-Label 分组,其核心目的是:

验证系统管理员可以对全局级标签执行完整的 CRUD(创建、读取、更新、删除)管理操作。

该用例同时以"用户指南"作为参考文档,并以"必须有一个运行中且可访问的 Harbor 实例"作为环境前提——这意味着整套验证既可以在真实部署环境中手工执行,也可以作为功能回归测试的基准场景。

测试步骤:系统管理员管理全局标签

步骤 1:系统管理员登录 UI

系统管理员(admin 或具备系统管理员角色的用户)登录 Harbor Web 门户。由于全局标签的增删改涉及系统级资源,只有具备相应系统权限的账号才能执行后续操作——这一点在源码的权限校验中会得到印证(详见下文"源码级原理"章节)。

步骤 2:创建全局级标签

登录后,进入标签管理页面,选择"全局级别"(Global)维度,填写标签名称(必填)、描述与颜色后提交,创建全局标签。

对应的 REST 调用为POST /api/v2.0/labels,请求体为LabelJSON 对象,成功时返回201 Created,并在响应头Location中给出新建资源的 URL(见 swagger.yaml)。

一个可复制的curl示例:

curl -k -u "admin:<password>" -H "Content-Type: application/json" \ -X POST "https://<harbor-host>/api/v2.0/labels" \ -d '{ "name": "production-ready", "description": "标识已验证可上生产的镜像", "color": "#00AA00", "scope": "g" }'

请求体中scope取值为g,表示创建全局标签。Level字段无需传参——服务端在CreateLabelHandler 中会强制将其置为LabelLevelUseru),并把ProjectID归零。

步骤 3:编辑已创建的标签

在标签列表中找到步骤 2 创建的标签,点击编辑,修改其名称、描述或颜色后保存。

对应的 REST 调用为PUT /api/v2.0/labels/{label_id}(见 swagger.yaml),请求体仍为LabelJSON 对象:

curl -k -u "admin:<password>" -H "Content-Type: application/json" \ -X PUT "https://<harbor-host>/api/v2.0/labels/1" \ -d '{ "name": "production-ready-v2", "description": "标识已验证可上生产的镜像(v2)", "color": "#0088CC" }'

需要注意:编辑操作只允许修改NameDescriptionColor三个属性,ScopeProjectID是不可变更的——这一限制直接由 Handler 实现决定(详见下文)。

步骤 4:删除已创建的标签

在标签列表中选中该标签并执行删除。删除前请注意:若该标签已被若干制品(artifact)引用,Harbor 会先自动解除标签与所有制品之间的引用关系,再删除标签本体。

对应的 REST 调用为DELETE /api/v2.0/labels/{label_id}

curl -k -u "admin:<password>" \ -X DELETE "https://<harbor-host>/api/v2.0/labels/1"

预期结果

测试用例对上述步骤给出了明确的预期:

  • 步骤 2:全局标签创建成功;
  • 步骤 3:全局标签更新成功;
  • 步骤 4:全局标签删除成功。

可能存在的问题

用例标注Possible Problems: None,即该场景在标准部署下无已知阻塞性问题。实操中若遇到 401(未认证/权限不足)、404(标签不存在)或 409(同名标签冲突),均可从下述源码行为中找到根因。

源码级原理:一次标签 CRUD 的完整调用链

围绕测试用例中的三步操作,我们顺着Handler → Manager → DAO → 数据模型/数据库的分层结构,逐一拆解底层实现。

REST Handler 层:权限校验与业务编排

所有标签相关的 REST 端点由 src/server/v2.0/handler/label.go 中的labelAPI处理,内部依赖label.Mgr(标签管理器)与project.Ctl(项目控制器)。

创建(CreateLabel),见 label.go#L51-L73:

label.Level = common.LabelLevelUser if label.Scope == common.LabelScopeGlobal { label.ProjectID = 0 } if err := lAPI.requireAccess(ctx, label, rbac.ActionCreate); err != nil { ... } id, err := lAPI.labelMgr.Create(ctx, label)
  • 服务端强制把Level置为u(用户级),防止客户端伪造系统级标签;
  • 全局标签(Scope = g)的ProjectID被强制归零;
  • 在入库前先执行requireAccess权限校验。

权限校验(requireAccess),见 label.go#L192-L203,是"只有系统管理员能管理全局标签"这一规则的具体落点:

switch label.Scope { case common.LabelScopeGlobal: return lAPI.RequireSystemAccess(ctx, action, rbac.ResourceLabel) case common.LabelScopeProject: return lAPI.RequireProjectAccess(ctx, label.ProjectID, action, subresources...) }

即:全局标签的任意操作(Create/Read/Update/Delete)都要求系统级ResourceLabel权限,而项目标签则校验该项目内的标签资源权限。这正是测试用例中"系统管理员"这一角色的权限基础。

更新(UpdateLabel),见 label.go#L140-L171:

label.Name = labelData.Name label.Description = labelData.Description label.Color = labelData.Color if err := label.Valid(); err != nil { ... } if err := lAPI.labelMgr.Update(ctx, label); err != nil { ... }

更新时仅将请求体中的NameDescriptionColor回写到从数据库读取的原标签对象上,随后重新执行Valid()校验并落库——ScopeProjectIDLevel等字段天然不可被修改。

删除(DeleteLabel),见 label.go#L173-L190:

id := label.ID if err := lAPI.labelMgr.RemoveFromAllArtifacts(ctx, id); err != nil { ... } if err := lAPI.labelMgr.Delete(ctx, id); err != nil { ... }

删除是"两步走":先调用RemoveFromAllArtifacts解除该标签与所有制品(artifact)的label_reference引用关系,再调用Delete删除标签记录,避免残留悬挂引用。

列表查询(ListLabels),见 label.go#L91-L138:scope参数必须是gp(否则返回 400);查询条件固定携带Level = "u"Scope;当scope = p时必须携带project_id,否则返回 400;名称支持模糊匹配(FuzzyMatchValue)。这意味着通过 API 查询全局标签时,请求形如:

curl -k -u "admin:<password>" \ "https://<harbor-host>/api/v2.0/labels?scope=g"

Manager 层:标签与引用的统一管理

src/pkg/label/manager.go 定义Manager接口,覆盖标签本身的生命周期(Create/Get/Count/Update/Delete/List)以及与制品的引用关系(ListByArtifact/AddTo/RemoveFrom/RemoveAllFrom/RemoveFromAllArtifacts)。

其中RemoveFromAllArtifacts(见 manager.go#L131-L138)通过查询条件Keywords: {"LabelID": labelID}批量删除label_reference表中的引用记录,这正是删除标签时"先清引用、再删本体"的实现出处。该包通过全局实例var Mgr = New()(manager.go#L27-L28)供 Handler 层直接注入使用。

DAO 层:ORM 持久化与唯一约束

src/pkg/label/dao/dao.go 是数据访问实现:

  • Create(dao.go#L76-L88):ormer.Insert(label)插入记录,若触发数据库唯一约束冲突,则包装为"label %s already exists"错误——同名全局标签会被拒绝(409);
  • Update(dao.go#L98-L112):先刷新UpdateTime再更新,同样处理重名冲突;
  • Delete(dao.go#L114-L129):按主键物理删除,删除 0 行时报 404;
  • ListByArtifact(dao.go#L143-L156):通过harbor_labellabel_reference联表查询某制品上的全部标签。

数据模型与数据库表结构

标签持久化涉及两张表,均由 ORM 注册(model.go#L26-L29):

  • harbor_label:标签主表,对应Label模型;
  • label_reference:标签与制品的引用关系表,对应Reference模型(LabelID+ArtifactID联合语义,见 model.go#L67-L74)。

建表 SQL 位于 make/migrations/postgresql/0001_initial_schema.up.sql,关键约束一目了然:

create table harbor_label ( id SERIAL NOT NULL, name varchar(128) NOT NULL, description text, color varchar(16), level char(1) NOT NULL, -- 's' 系统级 / 'u' 用户级 scope char(1) NOT NULL, -- 'g' 全局 / 'p' 项目 project_id int, creation_time timestamp default CURRENT_TIMESTAMP, update_time timestamp default CURRENT_TIMESTAMP, deleted boolean DEFAULT false NOT NULL, PRIMARY KEY(id), CONSTRAINT unique_label UNIQUE (name, scope, project_id) );

其中UNIQUE (name, scope, project_id)保证了"同一作用域下标签名唯一":因为全局标签的project_id恒为 0,所以全局标签之间不允许重名——这与 DAO 层将唯一约束冲突包装为"already exists"错误的逻辑完全对应。表上还创建了harbor_label_update_time_at_modtime触发器,在UPDATE时自动刷新update_time

harbor_resource_label表(见同文件后续定义)以及 2.0 版本迁移中引入的label_reference外键(见 0030_2.0.0_schema.up.sql),共同构成了"标签—资源"关联的演进基础:新版本中以 artifact 为粒度的label_reference替代了早期的资源标签表。

测试用例的价值:从手工验证到自动化回归

Group11-Label 目录下的测试用例(如本文件编号 11-01)是 Harbor 功能验证体系的一部分,与仓库中的 Robot Framework 用例(tests/robot-cases/)及 API 测试(tests/apitests/python/)相互补充。11-01 这类"单步 UI 操作 + 预期结果"的结构非常适合:

  • 手工验收:升级 Harbor 版本后快速回归标签管理功能;
  • 转化为自动化用例:可直接对照 swagger.yaml 中的POST /labelsPUT /labels/{label_id}DELETE /labels/{label_id}三个端点编写脚本化断言,与本文给出的curl示例一一对应;
  • 作为权限模型的验证锚点:确认系统级ResourceLabel权限的校验链路(label.go#L192-L203)没有被后续改动破坏。

小结

通过测试用例 11-01,我们验证了 Harbor 系统管理员对全局级标签的完整管理能力:UI 上三步操作即可完成创建、编辑与删除,对应 REST 层为POST /labelsPUT /labels/{label_id}DELETE /labels/{label_id}。从源码角度,Level(s/u)与Scope(g/p)双维度模型、Handler 层的系统权限强制校验、scope = gProjectID强制归零、更新仅允许修改名称/描述/颜色、删除前先解除制品引用等细节,共同构成了全局标签"仅系统管理员可管理、对所有项目可见、全局唯一命名"的行为边界。理解这条Handler → Manager → DAO → 数据库的调用链,不仅有助于排查标签相关的 400/404/409 错误,也能为你基于 Harbor 二次开发或编写自动化回归脚本提供直接依据。

【免费下载链接】harborAn open source trusted cloud native registry project that stores, signs, and scans content.项目地址: https://gitcode.com/GitHub_Trending/ha/harbor

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询