I have a need to programmatically (using golang) login to gcr.io docker registry using this package library https://godoc.org/github.com/docker/docker/client
I have tried using it, i can successfully login but upon pushing an image to my gcr.io project registry, it said
{"errorDetail":{"message":"unauthorized: You don't have the needed permissions to perform this operation, and you may have invalid credentials. To authenticate your request, follow the steps in: https://cloud.google.com/container-registry/docs/advanced-authentication"},"error":"unauthorized: You don't have the needed permissions to perform this operation, and you may have invalid credentials. To authenticate your request, follow the steps in: https://cloud.google.com/container-registry/docs/advanced-authentication"}
My code looks like this
package client
import (
"context"
"fmt"
"io"
"os"
"github.com/docker/docker/api/types"
dockerClient "github.com/docker/docker/client"
)
type Service struct{
DockerClient *dockerClient.Client
}
type CopyImageOptions struct {
DestRegistryAuth string
}
type DockerImageService interface {
CopyImage(ctx context.Context, source, dest string, option CopyImageOptions)
}
// NewDockerClient returns a client
func NewDockerClient() *Service {
cli, err := dockerClient.NewEnvClient()
if err != nil {
panic(err)
}
return &Service{DockerClient: cli}
}
func (s *Service) CopyImage(ctx context.Context, source, dest string, option CopyImageOptions) error {
rc, err := s.DockerClient.ImagePull(ctx, source, types.ImagePullOptions{})
if err != nil{
return fmt.Errorf("error when pulling source image. err: %v", err)
}
defer rc.Close()
io.Copy(os.Stdout, rc)
destClient := NewDockerClient()
if option.DestRegistryAuth != "" {
//current use case we can assume that the dest is on asia.gcr.io
status, err := destClient.DockerClient.RegistryLogin(ctx, types.AuthConfig{
Username: "oauth2accesstoken",
Password: option.DestRegistryAuth,
ServerAddress: "asia.gcr.io",
})
if err != nil{
return fmt.Errorf("error when login to destination image registry. err: %v", err)
}
fmt.Println(status)
}
err = destClient.DockerClient.ImageTag(ctx, source, dest)
if err != nil {
return fmt.Errorf("error when tagging image. err: %v", err)
}
rc, err = destClient.DockerClient.ImagePush(ctx, dest, types.ImagePushOptions{
RegistryAuth: option.DestRegistryAuth,
})
if err != nil{
return fmt.Errorf("error when pushing image to destionation. err: %v", err)
}
defer rc.Close()
io.Copy(os.Stdout, rc)
return nil
}
You may take a look at the CopyImage method, where the option.DestRegistryAuth is assigned with the output gcloud auth print-access-token. The username is set to "oauth2accesstoken" because I followed this instruction: https://cloud.google.com/container-registry/docs/advanced-authentication
As for the source parameter, it is assumed it's from public registry like docker.io/library/alpine:3.10, so we can pull it without having configuring any auth token. However for the dest parameter, currently it is an image in my private registry such as: asia.gcr.io/<gcp-project-id>/alpine:3.10
Also, the gcloud auth print-access-token is called after I did gcloud auth loginand I already had full permission to access my private asia.gcr.io registry (assigned on bucket level).
Now the weird thing is I can successfully push it using docker push command, right after do docker login described in here https://cloud.google.com/container-registry/docs/advanced-authentication .
Any advice?