Skip to content

Instantly share code, notes, and snippets.

@shamil
Forked from rjeczalik/gcr-tags.go
Last active Aug 22, 2016
Embed
What would you like to do?
Reads a tag list for Google Container Registry for the given image.
package main
import (
"encoding/base64"
"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)")
)
// Tag ...
type Tag struct {
Tag string `json:"tag"`
Image string `json:"image"`
}
// NewTags ...
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
}
// Auth ..
type Auth struct {
Auth string `json:"auth"`
Email string `json:"email"`
}
// AuthsObject ...
type AuthsObject map[string]Auth
// Config ...
type Config struct {
Auths AuthsObject `json:"auths"`
}
// UserPass ...
func (auths AuthsObject) UserPass(host string) (user, pass string, ok bool) {
for k := range auths {
u, err := url.Parse(k)
if err != nil {
continue
}
if u.Host != host {
continue
}
d, err := base64.StdEncoding.DecodeString(auths[k].Auth)
if err != nil {
fmt.Println("decode error:", err)
continue
}
auth := string(d)
if i := strings.IndexRune(auth, ':'); i != -1 {
return auth[:i], auth[i+1:], true
}
}
return "", "", false
}
// ReadConfig ...
func ReadConfig(file string) (AuthsObject, error) {
f, err := os.Open(file)
defer f.Close()
if err != nil {
return nil, err
}
var cfg Config
if err := json.NewDecoder(f).Decode(&cfg); err != nil {
return nil, err
}
return cfg.Auths, 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, ".docker/config.json")
cfg, err := ReadConfig(file)
if err != nil {
die("unable to read .docker/config.json:", 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] }
@shamil
Copy link
Author

shamil commented Aug 22, 2016

modified to support new docker config format

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