JFrog Artifactory Groovy 插件开发初探

2,431 阅读4分钟

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第 1 篇文章,点击查看活动详情


image.png

人们常对未知充满恐惧,甚至有时还未曾尝试,潜意识中便早已放弃。勇于尝试些自己不懂的事情,扩大知识边界,其实没有什么不可以!

                                                         ---- 程序员张三

1.需求背景

JFrog Artifactory 作为制品仓库,在内部上线已久,存储制品接近 200TB,近期接手运维工作,发现部分文件年代久远,其创建者早已离职。为方便追溯制品当前负责人,需要实现制品文件的 离职转交,例如,张三离职后,OA 流程自动触发接口,将其创建的所有制品,批量转交给李四。

运维部门是成本支出部门,每项对外提供的服务,都要对其进行计量计费,成本分摊。建立维护制品文件的责任人数据,更可以方便统计用户制品空间占用量,再按照部门聚合进行账单通知。

所以我们需要一个地方,存储制品的当前责任人,例如:owner=大飞哥

2.方案选择

查看官方接口文档,Artifactory API 制品信息中有 created_by 字段(UI 界面显示为 Deployed By),用户上传制品后,系统自动填充用户账号信息,但该字段无法修改。不过 Artifactory 为用户提供制品元数据属性,属性字段支持自定义及增删改查操作。

如何实现制品上传后,自动填充元数据属性呢?这正是此文要解决的问题。

👉 方案一

Artifactory 支持配置 Webhook,可以实现当用户上传制品时,回调自定义接口,在接口方法中实现制品属性配置。还可以为不同的制品仓库,配置不同的 webhook。

  • 优点: 实现比较灵活,不受开发语言限制
  • 缺点: 需要自行维护 API,运维成本提高

👉 方案二

通过 Artifactory Groovy 插件实现。Groovy 插件和 webhook 在本质上一样的,都是内部钩子方法的回调。参考官方文档 1

  • 优点: 运维成本低,内嵌在官方主程序中
  • 缺点: 官方插件开发缺少详细文档,Groovy 脚本陌生,开发成本高。

谁说没有枪头就捅不死人;谁说没学过 Groovy 就不能写脚本 😁

3. 插件实现

Groovy 是一种基于 JVM 的敏捷开发语言,可以与 Java 代码很好的结合,非常方便的扩展现有代码。Artifactory 插件本质上是注册一些回调方法,在某些特定时刻供 Artifactory 主程序调用。插件上下文会自动注入一些包,包中的类属性和类方法可以在 API 文档2中查询。

虽然官方文档并没有给出手把手的逐步教程,但社区维护的部分插件已开源在 Github 3。我们的插件就是参考了其中的 newFileWorkflow

// autoAddProperty.groovy
// 引入包
import org.artifactory.fs.ItemInfo
import org.artifactory.mime.MavenNaming
import org.artifactory.repo.RepoPath


// 判断当前制品是否要创建标签
// 远程仓库,Maven Meta数据,Maven Pom 文件则跳过
boolean applyTo(ItemInfo item) {
    RepoPath repoPath = item.repoPath
    !item.folder && !isRemote(repoPath.repoKey) &&
        !MavenNaming.isMavenMetadata(repoPath.path) &&
        !MavenNaming.isPom(repoPath.path)
}

// 判断当前制品仓库是否为远程仓库
// Artifactory 会为远程仓库创建一个 <仓库名>-cache 的缓存仓库
def isRemote(String repoKey) {
    if (repoKey.endsWith('-cache')) repoKey = repoKey.substring(0, repoKey.length() - 6)
    return repositories.getRemoteRepositories().contains(repoKey)
}


// 核心逻辑,注册 afterCreate 钩子回调方法
// ItemInfo 类所提供的属性和方法,见参考文档 3
storage {
    afterCreate { ItemInfo item ->
        try {
            if (applyTo(item)) {
                repositories.setProperty(item.repoPath, "owner", item.getCreatedBy())
            }
        } catch (Exception e) {
            log.error("Could not set property on $item", e)
        }
    }
}

30 行脚本而已,其实非常简单 !

4.插件部署

.groovy 文件拷贝到插件目录,默认在 /opt/jfrog/artifactory/var/etc/artifactory/plugins/ 目录中,对于 Artifactory 高可用集群,只需将文件上传到主节点的插件目录即可,其他从节点会自动同步。重启应用当然可以加载插件,生产环境中断服务显然不那么友好,官方提供通过 API 热加载插件,命令如下:

curl --request POST 'https://<artifactory>/artifactory/api/plugins/reload/' \
--header 'X-JFrog-Art-Api: <AccessToken>' \
--header 'Content-Type: text/plain' \
--data-raw ''

热加载接口将返回加载成功的插件列表和加载失败的插件列表(如果有的话)。

需求中提到的其他功能,大飞哥还在开发中,敬请关注我的掘金账号 !

Footnotes

  1. www.jfrog.com/confluence/…

  2. github.com/JFrog/artif…

  3. releases.jfrog.io/artifactory…