fix(registry): protect base images (alpine, node, rust) from retention script

This commit is contained in:
Ashwin Kumar Sivakumar 2026-06-11 01:15:54 +05:30
parent 827477ac3f
commit 3595de89c3

View file

@ -9,22 +9,51 @@ data:
REG='https://registry.nxtgauge.com'
CFG='/auth/.dockerconfigjson'
PATTERN=re.compile(r'^[0-9a-f]{40}$')
# Base images that MUST NEVER be deleted, even if their names start with
# nxtgauge- in the future. These are the FROM lines in our Dockerfiles
# (alpine for rust, node variants for frontend/admin, etc.). If any of
# these are missing the entire build pipeline breaks.
BASE_IMAGES = {
'alpine',
'node',
'rust',
'busybox',
'golang',
'nginx',
'postgres',
'redis',
}
# Project-image prefix that we DO prune. Anything outside this is sacred.
PROJECT_PREFIX = 'nxtgauge-'
with open(CFG,'r') as f:
dcfg=json.load(f)
auth=dcfg['auths']['registry.nxtgauge.com']['auth']
HEAD={'Authorization': f'Basic {auth}'}
def req(url, headers=None, method='GET'):
h=dict(HEAD)
if headers: h.update(headers)
r=urllib.request.Request(url, headers=h, method=method)
with urllib.request.urlopen(r, timeout=30) as resp:
return resp.status, dict(resp.headers), resp.read()
_, _, body = req(f'{REG}/v2/_catalog?n=1000')
repos=[r for r in json.loads(body.decode()).get('repositories',[]) if r.startswith('nxtgauge-')]
all_repos=json.loads(body.decode()).get('repositories',[])
# EXPLICIT SAFETY: only consider repos that match the project prefix.
# This double-belt-and-suspenders: base images (alpine/node/rust) are
# also in BASE_IMAGES as a fallback in case the prefix is ever changed.
repos=[r for r in all_repos if r.startswith(PROJECT_PREFIX) and r not in BASE_IMAGES]
# Sanity check: log if any base image is missing
missing_base = [b for b in BASE_IMAGES if b in all_repos or True] # always present
present = set(all_repos)
for b in BASE_IMAGES:
if b not in present:
print(f'[WARN] base image {b} not in registry catalog - re-push required!')
deleted=0
for repo in sorted(repos):
try:
@ -33,12 +62,12 @@ data:
except Exception as e:
print(f'[{repo}] tags/list failed: {e}')
continue
sha=[t for t in tags if PATTERN.match(t)]
if len(sha)<=1:
print(f'[{repo}] sha={len(sha)} no prune')
continue
rows=[]
for t in sha:
created='1970-01-01T00:00:00Z'
@ -54,7 +83,7 @@ data:
except Exception:
created='9999-12-31T23:59:59Z'
rows.append((created, t, digest))
rows.sort(key=lambda x: x[0], reverse=True)
KEEP_N=3 # keep last 3 SHA builds (was 1; bumped to prevent auth-blast-radius wipeouts)
keep_set=set(t for _, t, _ in rows[:KEEP_N])
@ -73,5 +102,5 @@ data:
print(f' delete failed {repo}:{t} code={e.code}')
except Exception as e:
print(f' delete failed {repo}:{t} err={e}')
print(f'deleted_manifests={deleted}')