对外开源是提高影响力、共建生态的有力手段。在项目对外开源的过程当中,首先是将能够开源的部分抽离出来,发布一个“开源版本”,与内部基础设施相关的部分则留在内部版本中。在后续的开发中,开源版本随着社区不断演进,内部版本则随着内部的需求不断迭代。若是没有明肯定义内外部版本的边界、代码同步不彻底、不及时,将会致使内外版本越走越远,变成两个不相干的项目。git
促使咱们开始作内外版本一致事情的,主要是如下三点考虑:github
本文将详细说明 PouchContainer 是如何作到内外部版本一致的。api
PouchContainer 开源已经将近一年了,在内外部版本差别梳理以前,没有人能讲清楚两个版本之间的差别。因而我用 Beyond Compare4 等软件,进行文件级别的比对,一个个找出内外部不一样的代码,进而梳理出内外部功能级别的差别。所幸内外部的差别比我想象中的要小。主要有如下几类差别:promise
前三类差别是集团内针对 PouchContainer 的定制化内容。固然,有一些是目前无可奈何加上的临时逻辑。在后续的发展中,将会改造、下线。第四类的差别,主要缘由在于开发者没有版本同步的意识:bugfix 在内部紧急修复以后,没有同步到社区。社区开发者的 bugfix, feature 没有及时同步进内部代码。网络
在差别梳理出来以后,须要明确内外部的功能边界。能开源的就开源,独有的功能继续保留。抹平非功能性的不一致。ide
__版本同步的根本仍是提高软件的可拓展性__,容许共用一套核心代码,针对不一样的业务场景进行定制。因此咱们面对的问题不只是内外部版本的一致问题,应该是三版本、四版本、多版本的一致问题。这也是 PouchContainer 赋能其余业务场景的基础。一样也是开源项目内部使用的必修课 因此,咱们作的第二步是经过__插件机制__提高 PouchContainer 的可拓展性。目前咱们支持 API, container, daemon, volume,cri 这五种插件。具体见文档。测试
插件的设计旨在提高软件的扩展性,但不容许插件改变原有的工做流程。PouchContainer 提供了对 container, daemon, volume, cri 关键步骤的 hook。以 daemon 插件为例,该插件提供了对启停接口的 hook。插件实现者能够在 daemon 启动前运行一些其余程序,好比 dfget。在 daemon 中止前,作一些清理操做。this
// DaemonPlugin defines places where a plugin will be triggered in pouchd lifecycle type DaemonPlugin interface { // PreStartHook is invoked by pouch daemon before real start, in this hook user could start dfget proxy or other // standalone process plugins PreStartHook() error // PreStopHook is invoked by pouch daemon before daemon process exit, not a promise if daemon is killed, in this // hook user could stop the process or plugin started by PreStartHook PreStopHook() error }
而 API 插件经过将路由表传给插件,容许插件实现者扩展、删除、修改 API。这让接口有了很大的灵活性。spa
import "github.com/alibaba/pouch/apis/server/types" // APIPlugin provide the ability to extend PouchContainer HTTP API and change how handler behave. type APIPlugin interface { // The default handler of each API would be passed in while starting HTTP server. // UpdateHandler could register extra HTTP API to PouchContainer server, // change the behavior of the default handler. UpdateHandler([]*types.HandlerSpec) []*types.HandlerSpec }
经过插件化的改造,绝大部分的内部 PouchContainer 定制化逻辑都在插件中实现了。插件单独一个文件目录,在代码合入的时候几乎不会产生冲突。以后将内部插件逻辑和其余差别一个个 commit 到开源分支上。__作到内外部版本的同源。__插件
PouchContainer 开源版本表明通用功能,若是外部开发者在通用版本上迭代的新增功能,集团内部用不到,该如何保障外部功能同步到内部以后,不影响内部的现有功能的?
首先内部版本是有一套完整的测试覆盖的,__内部测试在开源测试的基础上还包含针对内部场景的测试__。内部测试经过,咱们便认为该版本是符合内部场景要求的,开源版本没有影响内部稳定性的。若是测试不经过,有两个选择,一是从新评估开源通用功能,是否有代码缺陷;二是在内部仓库打补丁。以此来保障开源代码同步至内部后的稳定性。
咱们先来看看,在这以前的工做流是怎样的。开发者分别在内部仓库和开源仓库提代码,紧急一些的需求会先在内部仓库提 merge request,不那么重要的需求会先在社区提 PR。有人会按期将开源分支 merge 进内部仓库。这里有几个问题,一是内部也有一套测试流程,这套流程可能不像 travisCI 或 circleCI 对接 github 那么方便,有些设计好的测试在内部仓库甚至不会跑。二是没有插件化前,某些功能在内外部有两种不一样的实现,这在每次手动 merge 代码的时候几乎都会冲突,解决冲突的过程很容易引入非预期的修改,下次又继续冲突。
git flow 如图
在完成一致性改造以后,咱们创建一套规则来保证后续不会再出现版本分离的事情。
开源,能帮助项目吸取外部营养,加速项目的演进。在一致性的改造过程当中,帮助开发者明确内外部版本的边界,打造同一份核心代码,提高核心代码的可定制化能力,更好地服务于不一样的场景。