【问题标题】:Golang - Docker API - parse result of ImagePullGolang - Docker API - ImagePull 的解析结果
【发布时间】:2017-06-09 08:14:28
【问题描述】:

我正在开发一个Go 脚本,该脚本使用Docker API 用于我的项目。登录到我的存储库后,我提取了我想要的 Docker 映像,但问题是 ImagePull 函数返回了一个 io.ReadCloser 的实例,我只能通过以下方式将其传递给系统输出:

io.Copy(os.Stdout, pullResp)

我可以看到响应很酷,但是我找不到一种体面的方法来解析它并根据它实现一个逻辑,如果下载了新版本的图像,它会做一些事情,以及其他如果图像是最新的。
如果您分享您的经验,如果您曾经遇到过这个问题,我会很高兴。

【问题讨论】:

  • 响应是否有标准格式?如果是这样,您可以使用json.Decoder 将其解码为结构并根据结构中的值执行您想要的操作。
  • 其实它有点像流。我是 Go 的新手,所以我不太确定 :)
  • 你能添加一个带有示例响应输出的编辑吗,我没有在docker上工作过,只是有一个高水平的理解。
  • 我不能直接用fmt.Print()打印它,因为我得到&{0xc4202d42c0 {0 0} false <nil> 0x62f8b0 0x62f840},所以唯一的选择是将它传递给stdout,这给了我这样几行:@987654330 @。正如我之前所说,它类似于流,而不是静态数据,可以简单地解析为原生 JSON 对象。
  • 你能显示代码吗?

标签: docker go docker-api


【解决方案1】:

您可以导入github.com/docker/docker/pkg/jsonmessage 并同时使用JSONMessage 和JSONProgress 来解码流,但更容易调用 DisplayJSONMessagesToStream:它既解析流又将消息显示为文本。以下是使用 stderr 显示消息的方法:

    reader, err := cli.ImagePull(ctx, myImageRef, types.ImagePullOptions{})
    if err != nil {
            return err
    }
    defer reader.Close()

    termFd, isTerm := term.GetFdInfo(os.Stderr)
    jsonmessage.DisplayJSONMessagesStream(reader, os.Stderr, termFd, isTerm, nil)

好消息是它适应输出:如果这是一个 TTY(docker pull 的方式),它会更新行,但如果输出被重定向到文件,它不会。

【讨论】:

    【解决方案2】:

    @radoslav-stoyanov 在使用我的示例之前做

    # docker rmi busybox

    然后运行代码

    package main
    
    import (
        "encoding/json"
        "fmt"
        "github.com/docker/distribution/context"
        docker "github.com/docker/engine-api/client"
        "github.com/docker/engine-api/types"
        "io"
        "strings"
    )
    
    func main() {
        // DOCKER
        cli, err := docker.NewClient("unix:///var/run/docker.sock", "v1.28", nil, map[string]string{"User-Agent": "engine-api-cli-1.0"})
        if err != nil {
            panic(err)
        }
    
        imageName := "busybox:latest"
    
        events, err := cli.ImagePull(context.Background(), imageName, types.ImagePullOptions{})
        if err != nil {
            panic(err)
        }
    
        d := json.NewDecoder(events)
    
        type Event struct {
            Status         string `json:"status"`
            Error          string `json:"error"`
            Progress       string `json:"progress"`
            ProgressDetail struct {
                Current int `json:"current"`
                Total   int `json:"total"`
            } `json:"progressDetail"`
        }
    
        var event *Event
        for {
            if err := d.Decode(&event); err != nil {
                if err == io.EOF {
                    break
                }
    
                panic(err)
            }
    
            fmt.Printf("EVENT: %+v\n", event)
        }
    
        // Latest event for new image
        // EVENT: {Status:Status: Downloaded newer image for busybox:latest Error: Progress:[==================================================>]  699.2kB/699.2kB ProgressDetail:{Current:699243 Total:699243}}
        // Latest event for up-to-date image
        // EVENT: {Status:Status: Image is up to date for busybox:latest Error: Progress: ProgressDetail:{Current:0 Total:0}}
        if event != nil {
            if strings.Contains(event.Status, fmt.Sprintf("Downloaded newer image for %s", imageName)) {
                // new
                fmt.Println("new")
            }
    
            if strings.Contains(event.Status, fmt.Sprintf("Image is up to date for %s", imageName)) {
                // up-to-date
                fmt.Println("up-to-date")
            }
        }
    }
    

    您可以查看 API 格式来创建您的结构(例如我的 Event)以在此处阅读它们 https://docs.docker.com/engine/api/v1.27/#operation/ImageCreate

    希望能帮到你解决问题,谢谢

    【讨论】:

    • 谢谢@Vitaly!你的方法看起来不错,所以我会尝试一下,让你知道它是否适合我。
    • 因为我即将用赏金奖励您,如果您通过一个示例来改进您的答案,如果您下载了新版本的图像或现有的是最新的。
    • @RadoslavStoyanov 我已经更新了代码,我想你可以从中创建 func :-)
    【解决方案3】:

    我为我的目的使用了类似的方法(不是 moby 客户端)。通常,读取流响应的想法是相同的。试一试并实施你的。

    读取任何响应类型的流响应:

    reader := bufio.NewReader(pullResp)
    defer pullResp.Close()  // pullResp is io.ReadCloser
    var resp bytes.Buffer
    for {
        line, err := reader.ReadBytes('\n')
        if err != nil {
            // it could be EOF or read error
            // handle it
            break
        }
        resp.Write(line)
        resp.WriteByte('\n')
    }
    
    // print it
    fmt.Println(resp.String())
    

    但是,您在评论中的示例响应似乎是有效的 JSON 结构。 json.Decoder 是读取 JSON 流的最佳方式。这只是一个想法-

    type ImagePullResponse struct {
       ID             string `json"id"`
       Status         string `json:"status"`
       ProgressDetail struct {
         Current int64 `json:"current"`
         Total   int64 `json:"total"`
       } `json:"progressDetail"`
       Progress string `json:"progress"`
    }
    

    然后做

    d := json.NewDecoder(pullResp)
    for {
       var pullResult ImagePullResponse
      if err := d.Decode(&pullResult); err != nil {
        // handle the error
        break
      }
      fmt.Println(pullResult)
    }
    

    【讨论】:

      猜你喜欢
      • 2021-11-17
      • 2022-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多