从零开始阅读gitlab-runner源代码 002 DEBUG

前言

gitlab-runner是当前炙手可热的一个CICD工具,和Gitlab集成非常好。之前在工作中应用了一下,效果相当理想。

为了进一步了解gitlab-runner的运行原理,打算学习一下gitlab-runner源码。

由于是零基础,所以打算把整个过程记录下来,包括每一步的思路,以便复盘;贻笑大方之处,欢迎评论区打脸。

学习思路

一开始,我也不知道这堆代码是怎么划分模块的,所以从main.go开始,顺藤摸瓜,先对整个系统有个大致的了解,之后再有针对的学习gitlab-runner中核心的代码。

第一步:main.go

第一步先看main.go,主要有以下内容:

  • 异常处理:panic + recover + defer
  • cli_helpers:顾名思义,辅助cli的,不用关注。
  • 命令行框架:github.com/urfave/cli是一个golang的常见的命令行框架。用他可以很快捷的创建一整套命令体系。我们通过代码目录可以看到commands/下,保存了大部分command代码。这些command通过init,注册到common/command中的变量var commands []cli.Command

运行go run main.go,显示如下:

$ go run main.go
Runtime platform                                    arch=amd64 os=linux pid=19900 revision=HEAD version=development version
NAME:
   main - a GitLab Runner

USAGE:
   main [global options] command [command options] [arguments...]

VERSION:
   development version (HEAD)

AUTHOR:
   GitLab Inc. <[email protected]>

COMMANDS:
......

你可以手动试试go run main.go register,看看是什么。

第二步:register

register命令应该就是我们注册runner使用的命令,输入gitlab的token和url,然后在gitlab上注册一个runner实例。

使用命令go run main.go register,界面如下:

register

查看register.go代码:

  • register所需要的全部参数定义在type RegisterCommand struct {}
  • 结构体tag,如下``包括起来的内容,就是TagList的tag。看他tag的定义,可能会在反射中使用。
TagList           string `long:"tag-list" env:"RUNNER_TAG_LIST" description:"Tag list"`
  • register除了一堆和输入界面互动的代码,剩下的就是和gitlab通信,注册runner了。
func (s *RegisterCommand) askRunner() {
    
    
   ......
   result := s.network.RegisterRunner(s.RunnerCredentials, parameters)
   ......
}

我们看看network实现了什么。

common/network

network的接口定义如下,看上去他是专门负责和gitlab通信的。

type Network interface {
    
    
  RegisterRunner(config RunnerCredentials, parameters RegisterRunnerParameters) *RegisterRunnerResponse
  VerifyRunner(config RunnerCredentials) bool
  UnregisterRunner(config RunnerCredentials) bool
  RequestJob(config RunnerConfig, sessionInfo *SessionInfo) (*JobResponse, bool)
  UpdateJob(config RunnerConfig, jobCredentials *JobCredentials, jobInfo UpdateJobInfo) UpdateState
  PatchTrace(config RunnerConfig, jobCredentials *JobCredentials, content []byte, startOffset int) PatchTraceResult
  DownloadArtifacts(config JobCredentials, artifactsFile string, directDownload *bool) DownloadState
  UploadRawArtifacts(config JobCredentials, reader io.Reader, options ArtifactsOptions) UploadState
  ProcessJob(config RunnerConfig, buildCredentials *JobCredentials) (JobTrace, error)
}

根据这个接口,我找到了实现类./network/gitlab.goRegisterRunner流程如下:

//gitlab.go
func (n *GitLabClient) RegisterRunner(
   ......
  result, statusText, resp := n.doJSON(
    &runner,
    http.MethodPost,
    "runners",
    http.StatusCreated,
    &request,
    &response,
  )
  ......
}

func (n *GitLabClient) doJSON(
  credentials requestCredentials,
  method, uri string,
  statusCode int,
  request interface{
    
    },
  response interface{
    
    },
) (int, string, *http.Response) {
    
    
   ......
  return c.doJSON(uri, method, statusCode, request, response)
}

//client.go
func (n *client) doJSON(
  uri, method string,
  statusCode int,
  request interface{
    
    },
  response interface{
    
    },
) (int, string, *http.Response) {
    
    
   ......
   res, err := n.do(uri, method, body, jsonMimeType, headers)
   ......
}

func (n *client) do(
  uri, method string,
  request io.Reader,
  requestType string,
  headers http.Header,
) (*http.Response, error) {
    
    
   ......
  req, err := http.NewRequest(method, url.String(), request)
   ......
}

第三步:调试

下面我们使用VSCode,对上面的代码简单的进行一个跟踪调试。

修改launch.json

进入vscode的debug页面,点击小齿轮,配置一个launch.json:

{
    
    
    "version": "0.2.0",
    "configurations": [
        {
    
    
            "name": "register",
            "type": "go",
            "request": "launch",
            "mode": "debug",
            "program": "${workspaceFolder}",
            "args": [
                "register"
            ],
            "showLog": true
        }
    ]
}

debug

选中register(gitlab-runner),点击绿色箭头,开始debug:

launch.json

就可以轻松愉快的跟踪代码了:

runner debug

然后,好事多磨,这种调试模式,输出信息在Debug console中,不能模拟stdin,换句话说,无法进行信息的交互。

查找资料后,发现现有的VSCode插件都不支持debug模式下的stdin!!!

我们只能绕过这个坎,使用下面的debug模式:

attach 模式

祭出dlv工具。我们的解决方案是

  1. dlv在终端启动一个debug server
  2. VSCode采用remote模式,attach到上面的这个debug server
  3. dlv的终端输入命令,在VSCode进行代码的跟踪和调试。

在终端启动一个debug server

# 取消编译优化
go build -gcflags='all=-N -l'
# 启动debug server;
# --headless 指明是debug server
# --listen 指明运行的 IP:port
# -- log 记录日志
# --api-version=2 新版本都是2
# -- register 带入 os.args
dlv debug --headless --listen=127.0.0.1:9999  --log  --api-version=2  -- register

在VSCode新建一个debug config,配置如下:

{
    
    
    "version": "0.2.0",
    "configurations": [
        {
    
    
            "name": "attach",
            "type": "go",
            "request": "attach",
            "mode": "remote",
            "port": 9999,
            "host": "127.0.0.1",
        }
    ]
}

VSCode启动Debug,如下图:

vscode attach

一开始,因为dlv还没开始运行呢,所以Vscode也是没有跟踪到代码。按下F5,Debug Server就开始跑起来了。如下图:

debug server

在这个终端输入runner的相关参数,你就可以跟踪代码了。

总结

今天摸瓜了一天,大致看明白了gitlab-runner这套源代码的一个结构。

  • main.go:创建一个commands交互框架。
  • ./common:公共方法,通过外观模式,将几大模块对外开放。
  • ./commands:具体的命令行实现模块。
  • ./network:网络相关的,主要是和gitlab的通信。

另外,今天算是有了一个很好的Debug方案。

完毕。

猜你喜欢

转载自blog.csdn.net/weixin_36572983/article/details/108409712
002