在 docker 图像加载返回后立即检查时,通过 RepoTag 获取 ImageID 失败

问题描述 投票:0回答:1

我试图在加载 docker 镜像 tar 文件后立即获取 ImageID。但是

getImageIDByRepoTag
功能总是失败。图像按预期加载,我可以看到
docker images
并且可以看到图像。
image Load from tar
函数按预期工作。但是在图像加载失败后立即检查
getImageIDByRepoTag

func main() {
    // Set up Docker client
    cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
    if err != nil {
        panic(err)
    }

    // Path to your tar file
    tarFilePath := "mongo.tar"

    repoTag, err := loadImageFromTar(cli, tarFilePath)
    if err != nil {
        fmt.Printf("failed to load image from tar: %v", err)
        panic(err)
    }

    fmt.Printf("RepoTag: %s\n", repoTag)

    // sleep for 5 seconds to allow the image to load
    // time.Sleep(5 * time.Second)

    // Get the image ID
    imageID, err := getImageIDByRepoTag(cli, repoTag)
    if err != nil {
        fmt.Printf("failed to get image ID: %v\n", err)
    } else {
        fmt.Printf("Image ID: %s\n", imageID)
    }
}

func loadImageFromTar(cli *client.Client, tarFilePath string) (string, error) {
    // Read tar file
    tarFile, err := os.Open(tarFilePath)
    if err != nil {
        return "", fmt.Errorf("failed to open tar file: %w", err)
    }
    defer tarFile.Close()

    // Create a pipe to stream data between tar reader and Docker client
    pr, pw := io.Pipe()

    // Create a tar reader from the pipe reader
    tr := io.TeeReader(tarFile, pw) // reading `tr` will read the tarFile - but simultaneously write to `pw`

    // Set up a WaitGroup for synchronization
    var wg sync.WaitGroup
    wg.Add(2)

    // Set up error channels
    errChan1 := make(chan error, 1)
    errChan2 := make(chan error, 1)

    // Load the Docker image in a separate goroutine
    var imageLoadResponse types.ImageLoadResponse
    go func() {
        defer wg.Done()
        defer close(errChan1)

        imageLoadResponse, err = cli.ImageLoad(context.Background(), tr, false)

        fmt.Println("ImageLoadResponse: ", imageLoadResponse)
        if err != nil {
            errChan1 <- fmt.Errorf("failed to load Docker image: %w", err)
            return
        }

        // Close the image load response body
        defer imageLoadResponse.Body.Close()

        fmt.Println("Done loading image")
    }()

    // Read tar file metadata and copy the tar file to the pipe writer in a separate goroutine
    var repoTag string
    go func() {
        defer wg.Done()
        defer pw.Close()
        defer close(errChan2)

        tarReader := tar.NewReader(pr) // read from pipe (indirectly from the file)

        for {
            header, err := tarReader.Next()
            if err == io.EOF {
                break
            }
            if err != nil {
                errChan2 <- fmt.Errorf("failed to read tar header: %w", err)
                return
            }

            // Extract the repository and tag from the manifest file
            if header.Name == "manifest.json" {
                data, err := io.ReadAll(tarReader)
                if err != nil {
                    errChan2 <- fmt.Errorf("failed to read manifest file: %w", err)
                    return
                }

                var manifest []map[string]interface{}
                err = json.Unmarshal(data, &manifest)
                if err != nil {
                    errChan2 <- fmt.Errorf("failed to unmarshal manifest: %w", err)
                    return
                }

                repoTag = manifest[0]["RepoTags"].([]interface{})[0].(string)
            }
        }
    }()

    fmt.Println("Waiting for goroutines to finish...")

    // Wait for both goroutines to finish
    wg.Wait()

    // Check if any error occurred in the goroutines
    if err != nil {
        return "", err
    }

    // Check if any error occurred in the goroutines
    select {
    case err := <-errChan1:
        if err != nil {
            fmt.Printf("Error in errChan2: %v", err)
            return "", err
        }
    case err := <-errChan2:
        if err != nil {
            fmt.Printf("Error in errChan2: %v", err)
            return "", err
        }
    default:
    }

    return repoTag, nil
}

func getImageIDByRepoTag(cli *client.Client, repoTag string) (string, error) {
    images, err := cli.ImageList(context.Background(), types.ImageListOptions{})
    if err != nil {
        return "", fmt.Errorf("failed to list images: %w", err)
    }

    for _, image := range images {
        for _, tag := range image.RepoTags {
            if tag == repoTag {
                return image.ID, nil
            }
        }
    }

    return "", fmt.Errorf("image ID not found for repo tag: %s", repoTag)
}

但是如果我取消注释

time.Sleep(5 * time.Second)
程序工作/执行正常。 看起来在 docker 客户端的
load image
功能之后有延迟。

延迟/睡眠功能是否有任何解决方法,因为它可能因系统而异,基于 RAM 等。?

谢谢

docker go client
1个回答
0
投票

根据我的评论,在某些情况下客户端主体不会关闭,因此请确保在靠近呼叫站点的地方进行清理:

go func() {
    defer wg.Done()
    imageLoadResponse, err = cli.ImageLoad(context.Background(), tr, false)
    fmt.Println("ImageLoadResponse: ", imageLoadResponse)
    if err != nil {
        err = fmt.Errorf("failed to load Docker image: %w", err)
        fmt.Println(err)
        return
    }

    // call succeeded, so ensure we do clean-up ...

    defer imageLoadResponse.Body.Close() // <-- move this line here

    fmt.Println("Done loading image")
}()
© www.soinside.com 2019 - 2024. All rights reserved.