Instantly share code, notes, and snippets.
Reads a tag list for Google Container Registry for the given image.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
modified to support new docker config format