Skip to content

Instantly share code, notes, and snippets.

@rjeczalik
Last active October 4, 2018 11:37
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save rjeczalik/99ced542c0663f05f94c to your computer and use it in GitHub Desktop.
Save rjeczalik/99ced542c0663f05f94c to your computer and use it in GitHub Desktop.
Reads a tag list for Google Container Registry for the given image.
package main
import (
"encoding/json"
"flag"
"fmt"
"net/url"
"os"
"os/user"
"path/filepath"
"sort"
"strings"
"github.com/CenturyLinkLabs/docker-reg-client/registry"
)
var (
baseURL = flag.String("url", "https://gcr.io/v1/", "Docker Repository endpoint.")
basicUser = flag.String("user", "", "Username (read from ~/.dockercfg when empty)")
basicPass = flag.String("pass", "", "Password (read from ~/.dockercfg when empty)")
image = flag.String("image", "", "Docker image name - gcr.io/{image} (required)")
)
type Tag struct {
Tag string `json:"tag"`
Image string `json:"image"`
}
func NewTags(m registry.TagMap) []Tag {
tags := make([]Tag, 0, len(m))
for tag, image := range m {
tags = append(tags, Tag{Tag: tag, Image: image})
}
sort.Sort(byTagDesc(tags))
return tags
}
type Config map[string]struct {
Auth []byte `json:"auth"`
}
func (cfg Config) UserPass(host string) (user, pass string, ok bool) {
for k := range cfg {
u, err := url.Parse(k)
if err != nil {
continue
}
if u.Host != host {
continue
}
auth := string(cfg[k].Auth)
if i := strings.IndexRune(auth, ':'); i != -1 {
return auth[:i], auth[i+1:], true
}
}
return "", "", false
}
func ReadConfig(file string) (Config, error) {
f, err := os.Open(file)
if err != nil {
return nil, err
}
defer f.Close()
cfg := make(Config)
if err := json.NewDecoder(f).Decode(&cfg); err != nil {
return nil, err
}
return cfg, nil
}
func die(v ...interface{}) {
fmt.Fprintln(os.Stderr, v...)
os.Exit(1)
}
func userpass(host string) (usr, pass string) {
u, err := user.Current()
if err != nil {
die("unable to read current user:", err)
}
file := filepath.Join(u.HomeDir, ".dockercfg")
cfg, err := ReadConfig(file)
if err != nil {
die("unable to read .dockercfg:", err)
}
usr, pass, ok := cfg.UserPass(host)
if !ok {
die("no auth found for", host, "(try gcloud docker --authorize-only)")
}
return usr, pass
}
func main() {
flag.Parse()
if *image == "" {
die("image name is empty or missing")
}
u, err := url.Parse(*baseURL)
if err != nil {
die("unable to parse baseURL:", err)
}
if *basicUser == "" || *basicPass == "" {
*basicUser, *basicPass = userpass(u.Host)
}
c := registry.NewClient()
c.BaseURL = u
basic := registry.BasicAuth{
Username: *basicUser,
Password: *basicPass,
}
token, err := c.Hub.GetReadTokenWithAuth(*image, basic)
if err != nil {
die("failed to obtain read token:", err)
}
tags, err := c.Repository.ListTags(*image, token)
if err != nil {
die("failed to obtain tag list:", err)
}
p, err := json.MarshalIndent(NewTags(tags), "", "\t")
if err != nil {
die("failed to JSON encode tags:", err)
}
fmt.Printf("%s\n", p)
}
type byTagDesc []Tag
func (p byTagDesc) Len() int { return len(p) }
func (p byTagDesc) Less(i, j int) bool { return p[i].Tag > p[j].Tag }
func (p byTagDesc) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
@oguennec
Copy link

I am getting 403 failed to obtain read token: GET ... whatever the image I am querying.

$gcloud docker --authorize-only
Short-lived access for ['gcr.io', 'us.gcr.io', 'eu.gcr.io', 'asia.gcr.io', 'b.gcr.io', 'bucket.gcr.io', 'appengine.gcr.io'] configured.

$./gcr-tags -image gcr.io/google-containers/skydns
failed to obtain read token: GET https://gcr.io/v1/repositories/gcr.io/google-containers/skydns/images returned 403

@SleepyBrett
Copy link

@oguennec try $./gcr-tags -image google-containers/skydns

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment