Skip to content

Instantly share code, notes, and snippets.

@DazWilkin
Last active July 16, 2019 22:29
Show Gist options
  • Save DazWilkin/6ecc55068a8ef1b305d2113735a77424 to your computer and use it in GitHub Desktop.
Save DazWilkin/6ecc55068a8ef1b305d2113735a77424 to your computer and use it in GitHub Desktop.
Google Container Registry client using Docker Distribution
package main
import (
"context"
"flag"
"log"
"github.com/docker/distribution/registry/client"
heroku "github.com/heroku/docker-registry-client/registry"
"github.com/motemen/go-loghttp"
)
var (
username = flag.String("username", "_token", "username of the User's Registry account.")
password = flag.String("password", "", "password of the User's Registry account.")
registryURL = flag.String("registry", "https://gcr.io/v2", "URL of the Registry Service.")
project = flag.String("project", "", "GCP Project ID.")
)
func main() {
flag.Parse()
ctx := context.Background()
// NewRegistry takes http.RoundTripper
// Heroku implemented 2x RoundTrippers (Token|BasicTransport) for its client
// Reusing that code
// Unclear why, since BasicTransport includes a TokenTransport that both need username|password
// defaultTransport := http.DefaultTransport
logTransport := &loghttp.Transport{}
tokenTransport := &heroku.TokenTransport{
Transport: logTransport,
Username: *username,
Password: *password,
}
basicTransport := &heroku.BasicTransport{
Transport: tokenTransport,
URL: *registryURL,
Username: *username,
Password: *password,
}
// Also seemingly duplicative that NewRegistry takes a *registry and so does BasicTransport
// Regardless, this works
registry, err := client.NewRegistry(*registryURL, basicTransport)
if err != nil {
log.Fatal(err)
}
// Treating project as a starting point
// entries will include *project and subsequent repositories
// We'll grab only project and so set the slice size to 1
repositoryNames := make([]string, 1)
i, err := registry.Repositories(ctx, repositoryNames, *project)
//TODO(dazwilkin) Check that *project is returned
//TODO(dazwilkin) Check whether err is io.EOF
if err != nil {
log.Fatal(err)
}
log.Println(i)
// We decided to only return 1 repository but...
for j, repositoryName := range repositoryNames {
log.Printf("%v: %v", j, repositoryName)
// This is cumbersome but I assume it's because a myriad number of ways to represents repository names
// This implementation of NewNamed essentially maps X-->X :-)
name := NewNamed(repositoryName)
log.Println(name.Name())
repo, err := client.NewRepository(name, *registryURL, basicTransport)
if err != nil {
log.Fatal(err)
}
tagService := repo.Tags(ctx)
tagNames, err := tagService.All(ctx)
for _, tagName := range tagNames {
log.Println(tagName)
descriptor, err := tagService.Get(ctx, tagName)
if err != nil {
log.Fatal(err)
}
log.Println(descriptor)
}
}
}
package main
import (
"fmt"
"strings"
)
type Reference struct {
Image string
}
func (r Reference) String() string {
return r.Image
}
type Named struct {
Reference
Project string
}
func NewNamed(repo string) Named {
// E.g. myrepo/myimage
s := strings.Split(repo, "/")
return Named{
Reference: Reference{
Image: s[1],
},
Project: s[0],
}
}
func (n Named) Name() string {
// We decomposed it in NewNamed and are now just reassembling it
return fmt.Sprintf("%s/%s", n.Project, n.Reference.Image)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment