Skip to content

Instantly share code, notes, and snippets.

@wagoodman
Created March 30, 2022 13:25
Show Gist options
  • Save wagoodman/f66c9003daa4c472ac2a5eb2f28a9f7f to your computer and use it in GitHub Desktop.
Save wagoodman/f66c9003daa4c472ac2a5eb2f28a9f7f to your computer and use it in GitHub Desktop.
docker auth issue
package main
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strings"
"github.com/docker/cli/cli/config"
configTypes "github.com/docker/cli/cli/config/types"
"github.com/docker/cli/cli/connhelper"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/google/go-containerregistry/pkg/name"
)
func getClient() (*client.Client, error) {
var clientOpts = []client.Opt{
client.FromEnv,
client.WithAPIVersionNegotiation(),
}
host := os.Getenv("DOCKER_HOST")
if strings.HasPrefix(host, "ssh") {
var (
helper *connhelper.ConnectionHelper
err error
)
helper, err = connhelper.GetConnectionHelper(host)
if err != nil {
return nil, fmt.Errorf("failed to fetch docker connection helper: %w", err)
}
clientOpts = append(clientOpts, func(c *client.Client) error {
httpClient := &http.Client{
Transport: &http.Transport{
DialContext: helper.Dialer,
},
}
return client.WithHTTPClient(httpClient)(c)
})
clientOpts = append(clientOpts, client.WithHost(helper.Host))
clientOpts = append(clientOpts, client.WithDialContext(helper.Dialer))
}
if os.Getenv("DOCKER_TLS_VERIFY") != "" && os.Getenv("DOCKER_CERT_PATH") == "" {
err := os.Setenv("DOCKER_CERT_PATH", "~/.docker")
if err != nil {
return nil, fmt.Errorf("failed create docker client: %w", err)
}
}
dockerClient, err := client.NewClientWithOpts(clientOpts...)
if err != nil {
return nil, fmt.Errorf("failed create docker client: %w", err)
}
return dockerClient, nil
}
type pullEvent struct {
ID string `json:"id"`
Status string `json:"status"`
Error string `json:"error,omitempty"`
Progress string `json:"progress,omitempty"`
ProgressDetail struct {
Current int `json:"current"`
Total int `json:"total"`
} `json:"progressDetail"`
}
func main() {
imageStr := "alpine:latest"
c, err := getClient()
if err != nil {
panic(err)
}
var options = types.ImagePullOptions{}
cfg, err := config.Load("")
if err != nil {
panic(fmt.Errorf("failed to load docker config: %w", err))
}
ref, err := name.ParseReference(imageStr)
if err != nil {
panic(err)
}
hostname := ref.Context().RegistryStr()
authConfig, err := cfg.GetAuthConfig(hostname)
if err != nil {
panic(err)
}
options.RegistryAuth, err = encodeCredentials(authConfig)
if err != nil {
fmt.Printf("WARN: bad auth encode: %+v", err)
}
resp, err := c.ImagePull(context.Background(), imageStr, options)
if err != nil {
panic(fmt.Errorf("pull failed: %w", err))
}
var thePullEvent *pullEvent
decoder := json.NewDecoder(resp)
for {
if err := decoder.Decode(&thePullEvent); err != nil {
if err == io.EOF {
break
}
panic(fmt.Errorf("failed to pull image: %w", err))
}
fmt.Printf("%+v\n====\n", thePullEvent)
}
fmt.Println("complete!")
}
func encodeCredentials(authConfig configTypes.AuthConfig) (string, error) {
buffer := &bytes.Buffer{}
encoder := json.NewEncoder(buffer)
// note: the contents may contain characters that should not be escaped (such as password contents)
encoder.SetEscapeHTML(false)
if err := encoder.Encode(authConfig); err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(buffer.Bytes()), nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment