fix(postgres): 90-day retention, an install-time account name, and a restore drill #72
This file contains hidden or 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
| name: checks | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| jobs: | |
| placeholders: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| # main is the TEMPLATE: the placeholders belong here. This fails if a real | |
| # Azure identifier is committed over one, or a placeholder is deleted. | |
| - name: Placeholder template intact | |
| run: scripts/check-placeholders.sh --expect-template | |
| manifests: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: YAML parses | |
| run: | | |
| python3 - <<'PY' | |
| import pathlib, sys, yaml | |
| files = sorted(pathlib.Path('k8s').rglob('*.y*ml')) | |
| bad = [] | |
| for p in files: | |
| try: | |
| list(yaml.safe_load_all(p.read_text())) | |
| except yaml.YAMLError as e: | |
| bad.append(f"{p}: {e}") | |
| print(f"parsed {len(files)} files") | |
| if bad: | |
| print('\n'.join(bad), file=sys.stderr) | |
| sys.exit(1) | |
| PY | |
| pod-security: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| # PSA is applied per namespace by LABEL, so a namespace committed without | |
| # one is unprotected with nothing to notice it. See docs/security.md. | |
| - name: Project namespaces enforce Pod Security | |
| run: | | |
| python3 - <<'PY' | |
| import pathlib, re, sys, yaml | |
| ALLOWED = {'baseline', 'restricted'} # 'privileged' is not a level we ship | |
| bad, checked = [], 0 | |
| for p in sorted(pathlib.Path('k8s/projects').rglob('namespace*.y*ml')): | |
| for doc in yaml.safe_load_all(p.read_text()): | |
| if not doc or doc.get('kind') != 'Namespace': | |
| continue | |
| checked += 1 | |
| labels = (doc.get('metadata') or {}).get('labels') or {} | |
| level = labels.get('pod-security.kubernetes.io/enforce') | |
| if level is None: | |
| bad.append(f"{p}: no pod-security.kubernetes.io/enforce label") | |
| elif level not in ALLOWED: | |
| bad.append(f"{p}: enforce={level!r}, want one of {sorted(ALLOWED)}") | |
| ver = labels.get('pod-security.kubernetes.io/enforce-version') | |
| if not ver or not re.fullmatch(r'v\d+\.\d+', str(ver)): | |
| bad.append(f"{p}: enforce-version must be a pinned minor like v1.36, got {ver!r}") | |
| print(f"checked {checked} project namespace(s)") | |
| if not checked: | |
| print("no project namespaces found — the template must still be present", | |
| file=sys.stderr) | |
| sys.exit(1) | |
| if bad: | |
| print('\n'.join(bad), file=sys.stderr) | |
| sys.exit(1) | |
| PY | |
| secret-store-scope: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| # The store's identity can read the WHOLE vault, so which namespaces may use | |
| # it is the only limit. Every way of breaking that ends as a stalled | |
| # ExternalSecret, which nothing alerts on. See docs/security.md. | |
| - name: ClusterSecretStore is scoped, and every consumer is permitted by it | |
| run: | | |
| python3 - <<'PY' | |
| import pathlib, sys, yaml | |
| STORE = 'azure-kv' | |
| LABEL = 'scouterna.se/keyvault-access' | |
| # Only these filenames are synced by the project-infra ApplicationSet; | |
| # *.example reference files are deliberately ignored. | |
| SYNCED = ('namespace.yaml', 'developer-rbac.yaml', 'database.yaml') | |
| bad = [] | |
| def synced(p): | |
| return p.name in SYNCED or p.name.startswith(('namespace-', 'sealedsecret-')) | |
| # Index every ClusterSecretStore BY NAME. Checking "some store exists" | |
| # would pass if the one ExternalSecrets reference were renamed away. | |
| stores = {} | |
| for p in sorted(pathlib.Path('k8s/infra-manifest').rglob('*.y*ml')): | |
| for doc in yaml.safe_load_all(p.read_text()): | |
| if not doc or doc.get('kind') != 'ClusterSecretStore': | |
| continue | |
| conds = (doc.get('spec') or {}).get('conditions') or [] | |
| named, by_label = set(), False | |
| for c in conds: | |
| named |= set(c.get('namespaces') or []) | |
| sel = (c.get('namespaceSelector') or {}).get('matchLabels') or {} | |
| if sel.get(LABEL) == 'true': | |
| by_label = True | |
| stores[(doc.get('metadata') or {}).get('name')] = { | |
| 'path': p, 'conditions': conds, 'named': named, 'by_label': by_label} | |
| # 1. the store this repo depends on must exist, be scoped, and honour the | |
| # opt-in label — each of those failing separately breaks a different set | |
| # of ExternalSecrets at runtime, silently. | |
| if STORE not in stores: | |
| print(f"no ClusterSecretStore named {STORE!r} — every ExternalSecret in this " | |
| f"repo references it by name. Found: {sorted(stores) or 'none'}", | |
| file=sys.stderr) | |
| sys.exit(1) | |
| store = stores[STORE] | |
| if not store['conditions']: | |
| bad.append(f"{store['path']}: {STORE!r} has no spec.conditions — usable from " | |
| f"every namespace, which is the thing this check exists to prevent") | |
| if not store['by_label']: | |
| bad.append(f"{store['path']}: {STORE!r} has no namespaceSelector matching " | |
| f"{LABEL}: \"true\" — project namespaces carrying the label will " | |
| f"still be refused") | |
| # 2. every namespace consuming a ClusterSecretStore must be permitted by it: | |
| # named in conditions, or carrying the opt-in label (projects). | |
| labelled = {} | |
| for proj in sorted(pathlib.Path('k8s/projects').glob('*')): | |
| if not proj.is_dir() or proj.name == '_template': | |
| continue | |
| for p in sorted(proj.rglob('*.y*ml')): | |
| if not synced(p): | |
| continue | |
| for doc in yaml.safe_load_all(p.read_text()): | |
| meta = (doc or {}).get('metadata') or {} | |
| if (doc or {}).get('kind') == 'Namespace' and meta.get('name'): | |
| labelled[meta['name']] = ( | |
| (meta.get('labels') or {}).get(LABEL) == 'true', p) | |
| consumers = 0 | |
| for root in ('k8s/infra-manifest', 'k8s/projects'): | |
| for p in sorted(pathlib.Path(root).rglob('*.y*ml')): | |
| if p.name.endswith('.example') or (root == 'k8s/projects' and not synced(p)): | |
| continue | |
| for doc in yaml.safe_load_all(p.read_text()): | |
| if not doc or doc.get('kind') != 'ExternalSecret': | |
| continue | |
| ref = (doc.get('spec') or {}).get('secretStoreRef') or {} | |
| ns = (doc.get('metadata') or {}).get('namespace') | |
| if ref.get('kind') != 'ClusterSecretStore' or not ns: | |
| continue | |
| consumers += 1 | |
| target = stores.get(ref.get('name')) | |
| if target is None: | |
| bad.append(f"{p}: references ClusterSecretStore " | |
| f"{ref.get('name')!r}, which this repo does not define") | |
| continue | |
| if ns in target['named']: | |
| continue | |
| has_label, nsrc = labelled.get(ns, (False, None)) | |
| if has_label and target['by_label']: | |
| continue | |
| where = f"{nsrc}: " if nsrc else f"{p}: " | |
| why = (f"lacks {LABEL}: \"true\"" if nsrc | |
| else "is neither named in spec.conditions nor defined as a " | |
| "labelled namespace in this repo") | |
| bad.append(f"{where}namespace {ns!r} uses {ref.get('name')!r} " | |
| f"(see {p}) but {why} — the store will refuse it and the " | |
| f"ExternalSecret will stall") | |
| print(f"checked {len(stores)} ClusterSecretStore(s), {consumers} ExternalSecret(s) " | |
| f"consuming one; {STORE!r} allows {sorted(store['named'])} by name " | |
| f"+ label={store['by_label']}") | |
| if not consumers: | |
| print("no ExternalSecret references a ClusterSecretStore — expected several", | |
| file=sys.stderr) | |
| sys.exit(1) | |
| if bad: | |
| print('\n'.join(bad), file=sys.stderr) | |
| sys.exit(1) | |
| PY | |
| decisions-pointers: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| # Renumbering an entry silently breaks every pointer at it. Three of these | |
| # shipped across two PRs before this job existed. See docs/decisions.md. | |
| - name: decisions.md entry pointers resolve | |
| run: | | |
| python3 - <<'PY' | |
| import pathlib, re, subprocess, sys | |
| DEC = pathlib.Path('docs/decisions.md') | |
| text = DEC.read_text(encoding='utf-8', errors='replace') | |
| entries = {int(m.group(1)): m.group(2) | |
| for m in re.finditer(r'^## (\d+)\.\s+(.*)$', text, re.M)} | |
| if not entries: | |
| print('no numbered entries found in docs/decisions.md — has the ' | |
| 'heading format changed?', file=sys.stderr) | |
| sys.exit(1) | |
| bad, checked = [], 0 | |
| # 1. Numbering must be a gap-free run from 1. A gap means an entry was | |
| # removed or renumbered and something still points into the hole. | |
| nums = sorted(entries) | |
| if nums != list(range(1, len(nums) + 1)): | |
| bad.append(f"entry numbers are not 1..N with no gaps: {nums}") | |
| # 2. The index table must list every entry, and link to a real anchor. | |
| def slug(h): | |
| s = re.sub(r'[`*]', '', h.strip().lower()) | |
| s = re.sub(r'[^a-z0-9 -]', '', s) | |
| return re.sub(r'\s+', '-', s).strip('-') | |
| anchors = {slug(h) for h in entries.values()} | |
| anchors |= {slug(m.group(1)) | |
| for m in re.finditer(r'^#{2,4}\s+(.*)$', text, re.M)} | |
| for n, target in re.findall(r'\|\s*\[(\d+)\]\(#([^)]+)\)', text): | |
| checked += 1 | |
| if target not in anchors: | |
| bad.append(f"docs/decisions.md: index row [{n}] links to " | |
| f"#{target}, which is not a heading") | |
| listed = {int(n) for n, _ in re.findall(r'\|\s*\[(\d+)\]\(#([^)]+)\)', text)} | |
| for n in entries: | |
| if n not in listed: | |
| bad.append(f"docs/decisions.md: entry {n} has no index row") | |
| # 3. References INSIDE decisions.md are a bare "entry N". They must | |
| # resolve, and must not point at the entry they sit in — a | |
| # self-reference sends the reader back where they started. | |
| current = None | |
| for i, line in enumerate(text.splitlines(), 1): | |
| h = re.match(r'^## (\d+)\.', line) | |
| if h: | |
| current = int(h.group(1)) | |
| continue | |
| for m in re.finditer(r'\bentry (\d+)\b', line): | |
| n = int(m.group(1)) | |
| checked += 1 | |
| if n not in entries: | |
| bad.append(f"docs/decisions.md:{i}: entry {n} does not exist") | |
| elif n == current: | |
| bad.append(f"docs/decisions.md:{i}: entry {n} refers to " | |
| f"itself — did a renumber miss this?") | |
| # 4. References from EVERY OTHER tracked file name the document first. | |
| tracked = subprocess.run(['git', 'ls-files'], capture_output=True, | |
| text=True, check=True).stdout.split() | |
| ref = re.compile(r'decisions\.md\D{0,30}?entry (\d+)|decisions\.md (\d+)\b') | |
| for f in tracked: | |
| if f == 'docs/decisions.md': | |
| continue | |
| fp = pathlib.Path(f) | |
| if not fp.is_file(): | |
| continue | |
| try: | |
| body = fp.read_text(encoding='utf-8', errors='replace') | |
| except (OSError, UnicodeDecodeError): | |
| continue | |
| for i, line in enumerate(body.splitlines(), 1): | |
| m = ref.search(line) | |
| if not m: | |
| continue | |
| n = int(m.group(1) or m.group(2)) | |
| checked += 1 | |
| if n not in entries: | |
| bad.append(f"{f}:{i}: points at decisions.md entry {n}, " | |
| f"which does not exist (entries: 1..{max(entries)})") | |
| print(f"{len(entries)} entries, {checked} pointers checked") | |
| if bad: | |
| print('\n'.join(bad), file=sys.stderr) | |
| sys.exit(1) | |
| PY | |