6

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

io.Copy(os.Stdout, pullResp)

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

4

3 回答 3

7

您可以导入github.com/docker/docker/pkg/jsonmessage并使用JSONMessageJSONProgress来解码流,但调用 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),它会更新行,但如果输出被重定向到文件,它不会。

于 2018-02-02T09:45:36.917 回答
3

@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

我希望它可以帮助您解决您的问题,谢谢。

于 2017-06-12T22:05:38.783 回答
1

我为我的目的使用了类似的方法(不是 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)
}
于 2017-06-12T22:29:36.967 回答