41 lines
2.0 KiB
Python
41 lines
2.0 KiB
Python
"""HEAD each ref@digest at its registry (anonymous token). HEAD is not a pull and does not count
|
|
against Docker Hub's pull limit. Prints one line per ref: status + registry answer."""
|
|
import json, sys, urllib.request, urllib.error, re
|
|
ACC = "application/vnd.oci.image.index.v1+json,application/vnd.docker.distribution.manifest.list.v2+json,application/vnd.docker.distribution.manifest.v2+json,application/vnd.oci.image.manifest.v1+json"
|
|
def split(ref):
|
|
parts = ref.split("/")
|
|
if len(parts) == 1 or ("." not in parts[0] and ":" not in parts[0] and parts[0] != "localhost"):
|
|
reg, repo = "registry-1.docker.io", ref
|
|
if "/" not in repo: repo = "library/" + repo
|
|
else:
|
|
reg, repo = parts[0], "/".join(parts[1:])
|
|
if reg == "docker.io": reg = "registry-1.docker.io"
|
|
repo = repo.split(":")[0] if ":" in repo.split("/")[-1] else repo
|
|
return reg, repo
|
|
def head(reg, repo, dig, token=None):
|
|
req = urllib.request.Request(f"https://{reg}/v2/{repo}/manifests/{dig}", method="HEAD", headers={"Accept": ACC})
|
|
if token: req.add_header("Authorization", "Bearer " + token)
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=20) as r:
|
|
return r.status, r.headers
|
|
except urllib.error.HTTPError as e:
|
|
return e.code, e.headers
|
|
def token_for(hdrs, repo):
|
|
a = hdrs.get("www-authenticate") or ""
|
|
m = dict(re.findall(r'(\w+)="([^"]*)"', a))
|
|
if "realm" not in m: return None
|
|
url = f'{m["realm"]}?service={m.get("service","")}&scope=repository:{repo}:pull'
|
|
with urllib.request.urlopen(url, timeout=20) as r:
|
|
d = json.load(r)
|
|
return d.get("token") or d.get("access_token")
|
|
data = json.load(open(sys.argv[1]))
|
|
for ref, dig, srcs in data["with_digest"]:
|
|
reg, repo = split(ref)
|
|
try:
|
|
st, h = head(reg, repo, dig)
|
|
if st == 401:
|
|
st, h = head(reg, repo, dig, token_for(h, repo))
|
|
print(f"{st}\t{ref}@{dig[:19]}\t{','.join(srcs)}", flush=True)
|
|
except Exception as e:
|
|
print(f"ERR\t{ref}@{dig[:19]}\t{e}", flush=True)
|