第一个 Kata Containers 的 PR
终于到了该介绍如何在 Kata Containers 社区提交自己的 PR 了,可能有些人已经等不及了。读完这一章,你也可以尝试着提交个 PR 试试。
如何找到 issue
要想提交 PR,首先得确定要修改什么。
在 Kata Containers 社区通过 GitHub 的 issue 和 PR 来管理 bug 和合并请求。首先要有一个 issue,然后以此创建自己的开发分支,在本地开发、测试通过后,就可以提交 PR 到社区并获得项目维护者的 review,如果没有问题,就可以合并代码了。
大概有如下几种情况:
- 有人提了 issue,可以去 Kata Containers 的 issue 页面查找自己感兴趣的去 fix
- 自己使用中发现的 bug
- 自己需要的功能
- 学习、阅读源代码时候发现的问题
对于上面在阅读源代码时候发现的问题,也不完全都是 bug,也可以是简单的 typo,不合适的变量名,各种类似 golint 等对代码质量和阅读性上有影响的代码等,都可以作为修正对象。
在这篇文章中,我们以在阅读代码时发现的一个小改进为例,来看一下整体 PR 从提交到合并(merge)的过程 。
准备工作
配置 git
首先,需要对 git 进行简单的配置。
如果你的开源项目和工作项目需要分开,或者有多个账号要分开使用,那么推荐为不同的项目仓库配置不同的 git 用户信息(即标志一个代码贡献者)。
$ git config --local user.name zhangsan-lisi
$ git config --local user.email zhangsan-lisi@gmail.com
如果你已经设置了全集的用户信息(通过 --global
选项),并且不想单独设置不同的账号,则这一步可以省略。
开始我们的工作
介绍了一些提交 PR 的基础知识和前提,下面我们来进入真实的操作过程。
找到问题
在阅读源代码的时候,我们发现 kata_agent.go
中的 internalConfigure(h hypervisor, id string, config interface{})
,参数 config 是一个 interface 类型,而实际函数内部,这个 interface 貌似只有一种具体类型:
func (k *kataAgent) internalConfigure(h hypervisor, id string, config interface{}) error {
var err error
if config != nil {
switch c := config.(type) {
case KataAgentConfig:
if k.vmSocket, err = h.generateSocket(id); err != nil {
return err
}
k.keepConn = c.LongLiveConn
default:
return vcTypes.ErrInvalidConfigType
}
}
return nil
}
我们可以看到,这个参数只有一种实现的具体类型,感觉没有什么必要非得定义成一个接口。
Note: 在 Kata Containers 1.x 中有多种类型的 agent,所以在给 agent 是有的配置类型中,使用了接口定义。
所以,我们可以尝试将这一类方法中的 config 从接口类型改为具体的 KataAgentConfig
类型。
按照各函数的调用关系,我们可以找到 src/runtime/virtcontainers/agent.go
中 agent 接口的如下方法都需要修改:
- configure
- configureFromGrpc
最后修改的文件包括如下几个:
- src/runtime/virtcontainers/agent.go
- src/runtime/virtcontainers/kata_agent.go
- src/runtime/virtcontainers/mock_agent.go
如果能成功通过编译和测试,就可以大致验证我们的修改没有问题,因为我们没有修改任何逻辑和流程相关的内容,只是改了一下函数签名。
之后我们就可以准备提交代码了。只是按照现在的流程,在提交 PR 之前,需要先创建 issue。
创建 issue
在 issue 页面点击 New Issue 按钮,就可以看到下面的页面。
在 Kata Containers 中 issue 分为如下 3 类:
- Bug report:影响正常工作和使用的问题
- Enhancement request:虽然也能用,但是还有改进和优化
- Feature request:完全新的功能。
这里我们只是一个小小的代码改进,所以选择第二种的 Enhancement request。
默认创建 issue 页面提供了一个简易的模板,也可以使用自己的自由格式来描述。
主要包含包括如下一些信息:
- 要干什么
- 为什么要干
- 如何干
当然,issue 内容也可以比这更详细。总之根本原则之一就是要描述准确,让别人读完能理解即可。
最后我们创建的 issue 在这里。
修改代码
同步最新代码
在创建完 issue 之后,就可以开始准备修改代码了。
首先,修改代码前需要确保自己的代码是和 upstream 同步的,即保证自己 fork 的仓库和 Kata Containers 的仓库代码一致。这个可以通过如下命令实现:
$ git checkout main
$ git remote add upstream https://github.com/kata-containers/kata-containers.git
$ git fetch upstream
$ git merge upstream/main
创建 PR 分支
首先,每一个 PR 都需要有一个单独的分支,推荐使用有意义的分支名,比如可以使用如下格式:
<issue_id>/<pr-summarty>
比如我们这里使用如下命令创建新的分支。
$ git checkout -b 1610/use-concrete-kata-agent-config-type
Switched to a new branch '1610/use-concrete-kata-agent-config-type'
我们基于最新的代码创建了一个新的分支 1610/use-concrete-kata-agent-config-type
,并在此分支上修改代码。
修改代码
$ make
$ make test
提交代码
确认代码没有问题之后,就可以提交代码了。
$ git add .
$ git commit -s
commit
的 -s
参数用于 Signed-off-by
信息,这是 Kata Containers 的 commit 中需要遵循的规则。
比如这个例子中,我们输入的内容如下。
runtime: use concrete KataAgentConfig instead of interface type
Kata Containers 2.0 only have one type of agent, so there is no
need to use interface as config's type
Fixes: #1610
Signed-off-by: bin <bin@hyper.sh>
可以看出提交信息由 4 部分组成:
- 标题。这部分由 subsystem: commit summary 组成,根据具体的 pr 内容填写
- body 部分。和上面的标题隔开一行,记录具体的修改内容,用于对标题进行补充说明。可以是问题背景、如何修改、注意事项、以及其他参考资料等。
- fix 的 issue 编号。这部分以
Fixes:
开始,后面跟 issue 编号,如果有多个 issue, issue 之间用逗号分隔。 - Signed-off-by:这部分通过
commit -s
自动填写。
另外,Fixes 也可以指定仓库名,使用如下方式:
Fixes: github.com/kata-containers/kata-containers#1234
代码提交到本地后,下一步是需要 push 到 GitHub 的自己仓库之下。
首先我们可以不带参数尝试 push 一下:
$ git push
我们会看到默认的情况下这会失败,因为本地新建的分支还没有对应的远程分支。错误消息也会提示我们如何去做:
fatal: The current branch 1610/use-concrete-kata-agent-config-type
has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin 1610/use-concrete-kata-agent-config-type
直接使用错误信息中提示的命令推送即可:
$ git push --set-upstream origin 1610/use-concrete-kata-agent-config-type
创建 PR
代码推送到了自己 fork 的仓库后,就可以创建 pr 了。
到 Kata Containers 的 pr 页面 ,如果我们刚才 push 了分支到 fork 的仓库,这个页面会智能的提示你有新分支可以用了创建 pr,如下图所示:
直接点击 Compare & pull request 就可以进入到创建 pr 的页面了。如果没有上面的提示也没关系,你也可以点击 New pull request 进入这个页面。
创建 pr 的页面大致元素如下:
默认这个页面会使用代码中的提交信息分支填充一些信息。我们这里只有一个 commmit ,因此不需要做额外的修改就可以直接创建 pr ,如果你的 pr 修改内容很多,可能需要自己根据实际情况来补充完整。
上图 “1” 处表示的是从自己 fork 的仓库合并到 Kata Containers 的仓库, “2” 里绿色的 Able to merge 表示代码没有冲突,可以合并到目标分支(这里为 main
分支)。如果你这里显示的不是这样的结果,可能需要重新 rebase 一下主分支,以确保是在最新的代码上做的修改。
最后我们创建的 pr 在这里。
要想让你的代码获得合并,必须满足如下两个条件:
- 所有必须的 CI 测试通过
- 有 2 个或 2 个以上的项目维护者的 approve 。
CI 检查
CI 分两大类
- 使用 GitHub Action 的各种构建、静态检查和 UT 等验证
- 基于 Jenkins 的集成测试,包括基于最新代码和PR修改内容的所有组件的构建、各种环境(K8s、contaienrd、CRI-O、VFIO、QEMU、CLH、Firecracker等)的真实负载测试
对于各种静态检查,常见的问题有如下:
- commit message 不符合标准
- 没有代码格式化
- 单测不通过
对于这类错误,在 pr 页面底部的状态详情部分都会显示出来。对于失败的检查,可以点击后面的 Details 链接查看具体的错误信息。
如果真的有 CI 检查的错误,需要修正这些错误后,再次提交代码。这时候可以在原提交的基础上使用 --amend
重新提交,并使用 push -f
来强制更新远程分支。
在所有检查都通过后,就可以等待其他人的 review 和集成测试的结果。
代码 review
具有 review 权限的 Kata Containers 维护者负责 reivew 所有的 pr。有任何问题,他们都会直接在 GitHub 上以 comment 的情况提出来。 按 comment 需要进行的处理方式,主要分为两类:
- 讨论类
- 修正类
讨论类一般指 reivew 者所提出的疑问,比如这里为什么这么实现等。这类问题需要 pr 提交者进行回答和解释。
修正类指的是确实代码或文档存在错误,或可以改进的地方,这类问题需要 pr 提交者重新修改代码,提交后更新 pr 的内容。
集成测试
集成测试由项目维护成员来通过 /test
//test-ubuntu
等指令触发。集成测试有两种错误,一种是由于环境导致的错误,比如网络暂时的不可用等,这类错误都是可以重试的。另一类就是确实代码有问题,没有得到预期的结果,导致功能测试失败。这类错误需要 pr 提交者调查、修正后重新提交代码。
集成测试的代码都在 kata-containers/tests
下,每种测试都需要在全新的环境下从源代码开始构建各种二进制、guest 镜像等测试对象,所以整个耗时一般都在 30 - 60 分钟。
当然,如果只是修改了文档的话,则集成测试会很快返回,不会进行没有必要的测试。具体实现和方法可以参考 tests 仓库的 ci-fast-return.sh
文件 。
集成测试采用了 Jenkins 执行,每个测试任务的日志都可以在任务页面的 [Build Artifacts] 中找到,主要包括如下内容:
- kubelet 日志(如果包括 k8s 测试的话)
- containerd 日志(如果使用了 containred 的话)
- CRI-O 日志(如果使用了 CRI-O 的话)
- kernel 日志
- containerd-shim-kata-v2 日志
- kata-collect-data:测试技术后收集的系统(操作系统+额外依赖的软件+Kata Containers)信息
如果测试失败,可以下载相应的日志进行调查。
代码修改
不管是 reivew 人员指出的错误,还是在 CI 测试中发现的错误,都需要开发者在本地重新修改代码。
如果只有一个提交,可以直接使用 git commit --amend
和 git push -f
即可。如果是一个 pr 多个提交,则可能需要找到相应的提交进行 --amend
处理,本文中不对这样的例子进行说明。
关于提交粒度:对于比较大的 pr ,建议以单个功能点、单个顶层文件(src/runtime, src/agent, docs)分开提交,同时需要确保每个 commit 都能编译通过。
代码合并
如果所有必须的 CI 检查和测试都通过,也有足够数量的(目前是 2 人)项目维护者 approve 了 pr,则 pr 就可以合并了。合并只能有项目维护者执行。
合并前的 pr 状态是这样的:
从上图中,我们可以看到:
- Changes approved: 表示已经有足够多的项目维护者 approve 了代码
- All checks have passed: 所有的 GitHub actions 检查、CI 集成测试都通过
这时候,具有合并代码权限的项目维护者就可以通过 Merge pull request 按钮合并 pr 了。
代码合并后,对应的 issue 就自动的被 close 掉了。如果没有自动 close,则需要手工来 close 这个 issue。 Issue close 后,就算完成了一个简单的 pr 从开始修改、创建到合并的全部流程。
一些小技巧
很多项目维护者并不是专注于某一 issue 或 pr ,所以必要的时候,可能需要 pr 提交者主动“催”一下,可以在 pr 的 comment 栏里使用 @
来提醒你想要联系的项目维护者。
小结
这里我们以一个实例来介绍了如何创建一个 issue ,并进行修改、提交 pr,直到最后代码合并。当然这里是一个简单的 pr 流程,中间没有太多的变动。实际情况,根据 pr 的复杂程度和重要性,整个持续过程可能会达到数周或超过一个月甚至更久,可以说是一场持久战,这段时间都需要代码提交者和 reivew 者经过多次的讨论和确认,才能最终实现代码的合并,这也是一项考研持久性和耐力的活动。当然,毕竟这样的 pr 还是很少的。