#!/usr/bin/env python3 """Apply a strategic-merge patch to selected documents before kubectl apply.""" import argparse import os import re import subprocess import tempfile from pathlib import Path def field(document: str, name: str) -> str: match = re.search(rf"(?m)^{re.escape(name)}:\s*(\S+)\s*$", document) return match.group(1) if match else "" def metadata_name(document: str) -> str: match = re.search( r"(?ms)^metadata:\s*\n(?P(?:[ \t]+.*(?:\n|$))*)", document, ) if not match: return "" name = re.search(r"(?m)^[ \t]+name:\s*(\S+)\s*$", match.group("body")) return name.group(1).strip("\"'") if name else "" def main() -> None: parser = argparse.ArgumentParser() parser.add_argument("manifest", type=Path) parser.add_argument("--kind", action="append", required=True) parser.add_argument("--name") parser.add_argument("--patch", required=True) parser.add_argument("--require-match", action="store_true") args = parser.parse_args() documents = [ document.strip() for document in re.split(r"(?m)^---\s*$", args.manifest.read_text()) if document.strip() ] patched = 0 environment = dict(os.environ) environment["KUBECONFIG"] = os.devnull for index, document in enumerate(documents): if field(document, "kind") not in set(args.kind): continue if args.name and metadata_name(document) != args.name: continue with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml") as source: source.write(document + "\n") source.flush() result = subprocess.run( [ "kubectl", "patch", "--local", "-f", source.name, "--type=strategic", "--patch", args.patch, "-o", "yaml", ], check=True, env=environment, stdout=subprocess.PIPE, text=True, ) documents[index] = result.stdout.strip() patched += 1 if args.require_match and patched == 0: target = "/".join(args.kind) if args.name: target += f"/{args.name}" raise SystemExit(f"manifest does not contain required patch target {target}") args.manifest.write_text("\n---\n".join(documents) + "\n") if __name__ == "__main__": main()