Earthly源码解析

主流程:

完成buildkit配置文件构建并启动,Earthly config文件的构建,环境变量的读取,根据传入的target参数解析执行earthfile为buildkit接收的llb state,由buildkit完成最终的执行。

主流程核心代码:

Earthly是通过命令行APP的方式来运行的,cmd/earthly/main.go是程序的入口,Earthly使用https://github.com/urfave/cli来构建命令行APP。

func (app *earthlyApp) actionBuildImp(c *cli.Context, flagArgs, nonFlagArgs []string) error {
    // 解析命令行传入的target(local or github)
    target, err = domain.ParseTarget(targetName)
    // 构建并启动buildkitd服务
    bkClient, err := buildkitd.NewClient(c.Context, app.console, app.buildkitdImage, app.containerName, app.buildkitdSettings)
    // 根据用户输入参数构建核心的Builder结构
    b, err := builder.NewBuilder(c.Context, builderOpts)
    // 执行target的构建
    _, err = b.BuildTarget(c.Context, target, buildOpts)
}

// 根据传入的target执行构建
func (b *Builder) BuildTarget(ctx context.Context, target domain.Target, opt BuildOpt) (*states.MultiTarget, error) {
    mts, err := b.convertAndBuild(ctx, target, opt)
    if err != nil {
        return nil, err
    }
    return mts, nil
}

func (b *Builder) convertAndBuild(ctx context.Context, target domain.Target, opt BuildOpt) (*states.MultiTarget, error) {
    bf := func(childCtx context.Context, gwClient gwclient.Client) (*gwclient.Result, error) {
        // 通过buildkit将传入的earthfile数据结构转化为buildkit能够使用的LLB states
        mts, err = earthfile2llb.Earthfile2LLB(....)
    }
    err := b.s.buildMainMulti(ctx, bf, onImage, onArtifact, onFinalArtifact, onPull, "main")
}

// 通过buildkit api,用Build call back的方式,将上边的bf方法(eathfile转成LLB)、solveOpt(定义输出格式)作为参数,执行真正的build过程。
func (s *solver) buildMainMulti(...) {
    _, err = s.bkClient.Build(ctx, *solveOpt, "", bf, ch)
}

// 将arthfile文件通过[antlr](https://github.com/antlr/antlr4/tree/master/runtime/Go/antlr)解析成spec.Earthfile结构,方便进一步解析成buildkit的LLB 
func (r *Resolver) parseEarthfile(ctx context.Context, path string) (spec.Earthfile, error) {
    ast.Parse(ctx, k.(string), true)
}

// Earthfile struct是Earthfile文件的AST语法树表示
type Earthfile struct {
    Version        *Version        `json:"version,omitempty"`
    BaseRecipe     Block           `json:"baseRecipe"`
    Targets        []Target        `json:"targets,omitempty"`
    UserCommands   []UserCommand   `json:"userCommands,omitempty"`
    SourceLocation *SourceLocation `json:"sourceLocation,omitempty"`
}

// Statement对应Command
type Statement struct {
    Command        *Command        `json:"command,omitempty"`
    With           *WithStatement  `json:"with,omitempty"`
    If             *IfStatement    `json:"if,omitempty"`
    For            *ForStatement   `json:"for,omitempty"`
    SourceLocation *SourceLocation `json:"sourceLocation,omitempty"`
}

// 对应Earthfile文件一行行的命令,如COPY RUN等
type Command struct {
    Name           string          `json:"name"`
    Args           []string        `json:"args"`
    ExecMode       bool            `json:"execMode,omitempty"`
    SourceLocation *SourceLocation `json:"sourceLocation,omitempty"`
}

// earthfile2llb/interpreter.go Interpreter结合Converter完成核心的语法树解析功能,将spec.Command解析成对应的buildkit LLB
//
// 将earthfile命令解析成buildkit的llb.State(LLB)
func (i *Interpreter) handleCommand(ctx context.Context, cmd spec.Command) (err error) {
    switch cmd.Name {
    case "FROM":
        return i.handleFrom(ctx, cmd)
    case "RUN":
        return i.handleRun(ctx, cmd)
        ...
        ...
}

解析实例:

earthfile文件:

FROM golang:1.15-alpine3.13
WORKDIR /go-example
deps:
    COPY go.mod go.sum ./
    RUN go mod download

earthfile文件解析转化成Earthfile结构ast语法树,对应Json格式如下:

{
  "baseRecipe":  [
    {
      "command": {
        "args": [
          "golang:1.15-alpine3.13"
        ],
        "name": "FROM"
      }
    }
  ],
  "targets": [
    {
      "name": "deps",
      "recipe": [
        {
          "command": {
            "name": "COPY",
            "args": [
              "go.mod", "go.sum", "./"
            ]
          }
        },
        {
          "command": {
            "name": "RUN",
            "args": ["go", "mod", "download"]
          }
        }
}

Earthly日志输出

Earthly 通过buildkit的 ch := make(chan *client.SolveStatus) SolveStatus 管道,同步的读取buildkit的输出结果(logs), 通过solverMonitor解析buildkit输出,并对输出进行加工,形成自己的日志输出信息。Earthly的日志输出相对buildkit原生日志更直观,更好理解。

func (sm *solverMonitor) monitorProgress(ctx context.Context, ch chan *client.SolveStatus, phaseText string, sideRun bool) (string, error) {
    for {
        select {
        case ss, ok := <-ch:
            if !ok {
                break Loop
            }
            err := sm.processStatus(ss)
            if err != nil {
                return "", err
            }
        case <-sm.noOutputTicker.C:
            err := sm.processNoOutputTick()
            if err != nil {
                return "", err
            }
        }
    }
}

type SolveStatus struct {
    Vertexes []*Vertex
    Statuses []*VertexStatus
    Logs     []*VertexLog
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容