Drone 源码分析
安装运行
- Install go 1.9 or later
Install dependencies
go get -u github.com/drone/drone-ui/dist
go get -u golang.org/x/net/context
go get -u golang.org/x/net/context/ctxhttp
go get -u github.com/golang/protobuf/proto
go get -u github.com/golang/protobuf/protoc-gen-goInstall binaries to $GOPATH/bin
go install github.com/drone/drone/cmd/drone-agent
go install github.com/drone/drone/cmd/drone-server
相关介绍
建议先仔细阅读官方文档http://readme.drone.io/,
运行参考http://readme.drone.io/admin/installation-guide/,
- drone server,web服务
- drone agent, 代理服务,用于运行docker
其他一些组件
- drone/mq,a lightweight STOMP message broker
- drone cli,一堆的工具集合
server
在运行服务之前,需要准备参数,可以通过命令行查看,下面是一些搭建drone hello world的参考
- http://www.wtoutiao.com/p/38b2N4A.html
- https://overflow.no/blog/2016/11/14/self-hosted-cdci-environment-with-docker-droneio-and-drone-wall-on-debian-8/
- http://tleyden.github.io/blog/2016/02/15/setting-up-a-self-hosted-drone-dot-io-ci-server/
以github为例,必需的参数如下
- DRONE_OPEN=true,自动创建账户
- DRONE_GITHUB=true,使用github,可选的见【src/github.com/drone/drone/router/middleware/remote.go
- DRONE_GITHUB_CLIENT=${DRONE_GITHUB_CLIENT},从github网站获取
- DRONE_GITHUB_SECRET=${DRONE_GITHUB_SECRET},从github网站获取
- DRONE_SECRET=${DRONE_SECRET},用于agent连接server的key,具体通过drone/mq验证,使用websocket通道
代码中的定义:src/github.com/drone/drone/drone/server.go
// helper function to setup the remote from the CLI arguments.
func setupRemote(c *cli.Context) (remote.Remote, error) {
switch {
case c.Bool("github"):
return setupGithub(c)
case c.Bool("gitlab"):
return setupGitlab(c)
case c.Bool("bitbucket"):
return setupBitbucket(c)
case c.Bool("stash"):
return setupStash(c)
case c.Bool("gogs"):
return setupGogs(c)
default:
return nil, fmt.Errorf("version control system not configured")
}
}
不使用docker,直接写个脚本即可运行,为了使用依赖管理员权限的功能,增加DRONE_ADMIN,所有的记录存储在后端数据库,默认是sqlite3,当前目录下的drone.sqlite,为了避免在不同的目录出现问题,通过DRONE_DATABASE_DATASOURCE写死路径。
#!/bin/bash
export DRONE_OPEN=true
export DRONE_GITHUB=true
export DRONE_GITHUB_SECRET=22222
export DRONE_GITHUB_CLIENT=11111
export DRONE_SECRET=create_a_random_secret_here
export DRONE_DATABASE_DRIVER=sqlite3
export DRONE_DATABASE_DATASOURCE=/home/king/code/go/drone.sqlite
export DRONE_ADMIN=king1,king2
./drone "${@}"
支持的数据库见【src/github.com/drone/drone/store/datastore/store.go】
// helper function to setup the meddler default driver
// based on the selected driver name.
func setupMeddler(driver string) {
switch driver {
case "sqlite3":
meddler.Default = meddler.SQLite
case "mysql":
meddler.Default = meddler.MySQL
case "postgres":
meddler.Default = meddler.PostgreSQL
}
}
由于需要使用github的oauth认证,以及接收推送,所以服务器必须支持公网访问,若要本地测试,可以参考ngrok反向代理局域网开发机
agent
agent用于代理执行docker容器,由于docker运行需要超级管理员权限,所以需要sudo,或者切换到root。为了方便测试,最好是将当前用户添加到docker组,参考https://my.oschina.net/zhouhui321/blog/788431
sudo gpasswd -a ${USER} docker
sudo service docker restart
agent完整参数见【src/github.com/drone/drone/drone/agent/agent.go】,必需的参数如下
- DRONE_SERVER=ws://${drone-server}/ws/broker,server的路径
- DRONE_SECRET=${DRONE_SECRET},在server中配置的token
同样可以export两个环境变量之后,直接drone agent即可
cli
运行上述两个后台之后,就可以用浏览器访问【http//${drone-server}】,若未登录点击【登录/login】会自动跳转到github进行认证,由于设置了【DRONE_OPEN=true】所以认证成功之后,会自动创建以github用户名的账户。
drone并未使用普通的session管理机制,而是使用jwt机制,并使用cookie传输,如下
user_sess=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEuNDg5MDUxNjc5ZSswOSwidGV4dCI6InFqdyIsInR5cGUiOiJzZXNzIn0.SD1uwHk_E7yT-74sVXHwrLPKmonbwRtQTzT8cNPrupg
_crsf=MTQ4NzkyMTYzOHxEdi1CQkFFQ180SUFBUkFCRUFBQU12LUNBQUVHYzNSeWFXNW5EQW9BQ0dOemNtWlRZV3gwQm5OMGNtbHVad3dTQUJCS2IzSk5lbVEwWlRaV05XdHdUalZrfHeLaVDFFSGOkLA19-4Y6G5gYkzJatkOn8oNnHj4eb6q;
cli运行依赖的参数包括
- DRONE_SERVER=http://${drone-server} ,server的地址,注意名称和agent的一样,但地址并不一致
- DRONE_TOKEN=fsajdflkas,token就是jwt的token,注意和DRONE_SECRET区别
我们可以直接将网站的jwt token直接拿来使用,但仅限于查询操作,其他操作会因为csrf限制无权限,参考【src/github.com/drone/drone/router/middleware/session/user.go】
// if this is a session token (ie not the API token)
// this means the user is accessing with a web browser,
// so we should implement CSRF protection measures.
if t.Kind == token.SessToken {
err = token.CheckCsrf(c.Request, func(t *token.Token) (string, error) {
return user.Hash, nil
})
// if csrf token validation fails, exit immediately
// with a not authorized error.
if err != nil {
c.AbortWithStatus(http.StatusUnauthorized)
return
}
}
这里实际上引申出一个session type的东西
const (
UserToken = "user"
SessToken = "sess"
HookToken = "hook"
CsrfToken = "csrf"
AgentToken = "agent"
)
网页授权使用的是SessionToken,而drone cli使用UserToken,具体通过drone网站右上角【ACCOUNT】,再回到左上角【SHOW TOKEN】,参考【src/github.com/drone/drone/router/router.go】
user := e.Group("/api/user")
{
user.POST("/token", server.PostToken)
user.DELETE("/token", server.DeleteToken)
}
cli是一堆的命令集合,具体参考官方文档,有些功能仅限cli使用
普通账户密码登录
drone代码中有一些相关的逻辑,但并不完整,所以不可用
// src/github.com/drone/drone/router/router.go
e.GET("/login", server.ShowLogin)
e.GET("/login/form", server.ShowLoginForm)
e.GET("/logout", server.GetLogout)
// src/github.com/drone/drone/server/pages.go
// ShowLoginForm displays a login form for systems like Gogs that do not
// yet support oauth workflows.
func ShowLoginForm(c *gin.Context) {
c.HTML(200, "login.html", gin.H{})
}
// src/github.com/drone/drone/server/template/files/login.html