Reputation: 3794
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 login
and 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?
Upvotes: 5
Views: 6582
Reputation: 580
For anyone landing on this now, Agung's comment is still 90% correct, however, they've moved the AuthConfig
into the registry
types. So you now need to have:
import (
"github.com/docker/docker/api/types/registry"
)
Then in your code, you'd use:
func YourFunction(username string, password string) {
// some code
authConfig := registry.AuthConfig{
Username: username,
Password: password,
}
// other code
}
One final note is that you no longer pass this in to the cleint, you pass it into the functions you call as an option.
The link given by Agung has the updated information.
Upvotes: 0
Reputation: 3794
Okay I just found out what the mistake is on my code above. I realized this after looking at example code on pulling image from private registry here: https://docs.docker.com/develop/sdk/examples/#pull-an-image-with-authentication
As it turns out, the RegistryAuth
arg in types.ImagePush options expect a base64 encoding string.
So with this code, I can successfully push local image to my private registry.
authConfig := types.AuthConfig{
Username: "oauth2accesstoken",
Password: option.DestRegistryAuth,
}
encodedJSON, err := json.Marshal(authConfig)
if err != nil {
return fmt.Errorf("error when encoding authConfig. err: %v", err)
}
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
rc, err = destClient.DockerClient.ImagePush(ctx, dest, types.ImagePushOptions{
RegistryAuth: authStr,
})
Upvotes: 6