chore: internal docker package with handy PullImage and CreateContainerWithImagePull methods

This commit is contained in:
Pasha Sviderski
2025-09-26 21:24:56 +10:00
parent 20cf98d42e
commit 27bbbe7186
4 changed files with 152 additions and 35 deletions
+100
View File
@@ -0,0 +1,100 @@
package docker
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"os"
dockercommand "github.com/docker/cli/cli/command"
dockerconfig "github.com/docker/cli/cli/config"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/pkg/jsonmessage"
)
type PullImageMessage struct {
Message jsonmessage.JSONMessage
Err error
}
// PullImage pulls a Docker image and returns a channel to receive progress messages.
func (cli *Client) PullImage(
ctx context.Context, image string, opts image.PullOptions,
) (<-chan PullImageMessage, error) {
if opts.RegistryAuth == "" {
// Try to retrieve the authentication token for the image from the default local Docker config file.
if encodedAuth, err := RetrieveLocalDockerRegistryAuth(image); err == nil {
opts.RegistryAuth = encodedAuth
}
}
respBody, err := cli.ImagePull(ctx, image, opts)
if err != nil {
return nil, err
}
defer respBody.Close()
decoder := json.NewDecoder(respBody)
ch := make(chan PullImageMessage)
go func() {
defer close(ch)
var jm jsonmessage.JSONMessage
for {
if err = decoder.Decode(&jm); err != nil {
if errors.Is(err, io.EOF) {
break
}
ch <- PullImageMessage{Err: fmt.Errorf("decode image pull message: %w", err)}
break
}
msg := PullImageMessage{Message: jm}
if jm.Error != nil {
msg.Err = errors.New(jm.Error.Message)
}
select {
case err = <-ctx.Done():
ch <- PullImageMessage{Err: ctx.Err()}
return
default:
ch <- msg
}
}
}()
return ch, nil
}
// RetrieveLocalDockerRegistryAuth retrieves the authentication token for the specified image from the local Docker
// config file. It returns the encoded authentication token if it contains any credentials, or an empty string if
// no credentials are found.
func RetrieveLocalDockerRegistryAuth(image string) (string, error) {
// Try to retrieve the authentication token for the image from the default local Docker config file.
dockerConfig := dockerconfig.LoadDefaultConfigFile(os.Stderr)
encodedAuth, err := dockercommand.RetrieveAuthTokenFromImage(dockerConfig, image)
if err != nil {
return "", err
}
// The encodedAuth can be a base64-encoded "{}" (empty JSON object) or include a server address but no credentials.
// Return encodedAuth only if it contains any credentials.
auth, err := registry.DecodeAuthConfig(encodedAuth)
if err != nil {
return "", fmt.Errorf("decode auth config: %w", err)
}
if auth.Username == "" &&
auth.Password == "" &&
auth.Auth == "" &&
auth.IdentityToken == "" &&
auth.RegistryToken == "" {
return "", nil
}
return encodedAuth, nil
}