@mark1russell7/kind 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["s","seconds","deletesAPod"],"sources":["../src/backends.ts","../src/scenario.ts","../src/tools.ts","../src/cluster.ts","../src/compose.ts","../src/faults.ts","../src/image.ts","../src/lab.ts","../src/manifests.ts","../src/predict.ts","../src/rules.ts","../src/run.ts"],"sourcesContent":["// The parser of the rules of kube-proxy. The agent of each node reads the rules with it, and the\n// image copies this file, thus it imports nothing.\n\n/** A service rule names the Service whose backends the agent reads: its namespace, its name, and its ClusterIP and port for IPVS. */\nexport interface ServiceRule {\n readonly mode: \"iptables\" | \"ipvs\" | \"nftables\";\n readonly ns: string;\n readonly svc: string;\n readonly ip: string;\n readonly port: number;\n}\n\nconst hexIp = (hex: string): string =>\n [0, 2, 4, 6].map((at) => parseInt(hex.slice(at, at + 2), 16)).join(\".\");\n\n/**\n * This function gives the backends of a Service in a dump of the rules of kube-proxy, as\n * `ip:port`, sorted. A backend of IPVS with the weight zero takes no new connection, thus it is\n * not a backend.\n * - iptables: the `KUBE-SVC` chain of the Service jumps to a `KUBE-SEP` chain for each backend, and that chain does the DNAT.\n * - nftables: the `service-` chain of the Service does the DNAT to each backend, or it names an `endpoint-` chain for each one.\n * - IPVS: `/proc/net/ip_vs` has the line of the virtual service, then a line for each real server.\n */\nexport const backendsOf = (mode: ServiceRule[\"mode\"], text: string, rule: Pick<ServiceRule, \"ns\" | \"svc\" | \"ip\" | \"port\">): string[] => {\n const out = new Set<string>();\n const lines = text.split(\"\\n\");\n if (mode === \"iptables\") {\n const seps = new Set<string>();\n for (const line of lines) {\n if (!line.startsWith(\"-A KUBE-SVC-\") || !line.includes(`${rule.ns}/${rule.svc}:`)) continue;\n const jump = / -j (KUBE-SEP-\\S+)/.exec(line);\n if (jump !== null) seps.add(jump[1]!);\n }\n for (const line of lines) {\n const chain = /^-A (KUBE-SEP-\\S+) /.exec(line);\n if (chain === null || !seps.has(chain[1]!)) continue;\n const to = / --to-destination (\\S+)/.exec(line);\n if (to !== null) out.add(to[1]!);\n }\n } else if (mode === \"nftables\") {\n // a chain name with a slash can print in quotes: `chain \"service-…\" {` and `goto \"endpoint-…\"`\n let inside = false;\n const ours = `-${rule.ns}/${rule.svc}/`;\n for (const line of lines) {\n const head = /^\\s*chain\\s+\"?([^\"\\s{]+)\"?\\s*\\{/.exec(line);\n if (head !== null) inside = head[1]!.startsWith(\"service-\") && head[1]!.includes(ours);\n else if (/^\\s*(set|map)\\s/.test(line)) inside = false;\n if (!inside) continue;\n // one chain for each endpoint, as kube-proxy 1.31 writes it\n for (const match of line.matchAll(/(?:goto|jump)\\s+\"?endpoint-[0-9A-Za-z]+(-[^\\s,}\"]+)__([0-9.]+)\\/(\\d+)\"?/g))\n if (match[1]!.startsWith(ours)) out.add(`${match[2]}:${match[3]}`);\n // a DNAT in the service chain, as kube-proxy 1.37 writes it: `numgen random mod 2 map { 0 : IP . PORT, … }`, or `dnat to IP:PORT`\n for (const match of line.matchAll(/(?:^|[\\s{,:])(\\d+\\.\\d+\\.\\d+\\.\\d+) \\. (\\d+)(?=\\s*[,}]|\\s*$)/g)) out.add(`${match[1]}:${match[2]}`);\n for (const match of line.matchAll(/\\bdnat (?:ip )?to (\\d+\\.\\d+\\.\\d+\\.\\d+):(\\d+)/g)) out.add(`${match[1]}:${match[2]}`);\n }\n } else {\n let inside = false;\n for (const line of lines) {\n // each virtual service starts a block: a UDP or an SCTP one ends the block of the TCP service before it\n const service = /^(TCP|UDP|SCTP)\\s+([0-9A-F]{8}):([0-9A-F]{4})\\s/.exec(line);\n if (service !== null) {\n inside = service[1] === \"TCP\" && hexIp(service[2]!) === rule.ip && parseInt(service[3]!, 16) === rule.port;\n continue;\n }\n const real = /^\\s+->\\s+([0-9A-F]{8}):([0-9A-F]{4})\\s+\\S+\\s+(\\d+)/.exec(line);\n if (inside && real !== null && Number(real[3]) > 0) out.add(`${hexIp(real[1]!)}:${parseInt(real[2]!, 16)}`);\n }\n }\n return [...out].sort();\n};\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { valueIn } from \"@mark1russell7/quantity\";\n\n/** A kube-proxy mode is the dataplane of each node: iptables (the default of kind), IPVS or nftables. */\nexport type KubeProxyMode = \"iptables\" | \"ipvs\" | \"nftables\";\nexport const KUBE_PROXY_MODES: readonly KubeProxyMode[] = [\"iptables\", \"ipvs\", \"nftables\"];\n\n/** A node image is the kind node image of one Kubernetes version, pinned by digest. */\nexport interface NodeImage {\n readonly kubernetes: string;\n readonly image: string;\n}\n\n/**\n * These are the node images of kind v0.33.0, from its release notes. The image of Kubernetes 1.37\n * is v1.37.0. The research names v1.37.1 for some defaults, thus a default that changed in the\n * patch release is a mismatch to record.\n */\nexport const NODE_IMAGES: Readonly<Record<string, NodeImage>> = {\n \"1.37\": {\n kubernetes: \"v1.37.0\",\n image: \"kindest/node:v1.37.0@sha256:a1ed56cfb0e7b93589bdf97c8cd566405a265939e3620fc4f5de89adff580ae5\",\n },\n \"1.36\": {\n kubernetes: \"v1.36.4\",\n image: \"kindest/node:v1.36.4@sha256:099e049362a1526b2db71494e1947aae99bd16290d7c895f2b7ea312e3cbfaed\",\n },\n};\n\n/**\n * A scenario is one kind cluster as data. The lab generates the configuration of kind from it, and\n * nothing comes from a default of kind. The scenario gives the node count, the version, the mode\n * of kube-proxy and its sync periods, the CNI and the subnets.\n */\nexport interface KindScenario {\n /** This name names the cluster and its Docker network: lowercase letters, digits and dashes. */\n readonly name: string;\n /** This key selects the node image (`NODE_IMAGES`). */\n readonly kubernetes: string;\n /** This count is the number of workers. The cluster also has one control plane. */\n readonly workers: number;\n readonly kubeProxy: {\n readonly mode: KubeProxyMode;\n /** This time is the shortest interval between two syncs of the rules. */\n readonly minSyncPeriod: Quantity;\n /** This time is the interval of the full sync of the rules. */\n readonly syncPeriod: Quantity;\n };\n /** This CNI is kindnet, the CNI of kind. Cilium comes later. */\n readonly cni: \"kindnet\";\n readonly podSubnet: string;\n readonly serviceSubnet: string;\n}\n\n/** This function gives a scenario with the defaults of Kubernetes for kube-proxy, as explicit values. */\nexport const scenario = (name: string, mode: KubeProxyMode, workers: number, kubernetes = \"1.37\"): KindScenario => ({\n name,\n kubernetes,\n workers,\n kubeProxy: { mode, minSyncPeriod: { value: 1, unit: \"s\" }, syncPeriod: { value: 30, unit: \"s\" } },\n cni: \"kindnet\",\n podSubnet: \"10.244.0.0/16\",\n serviceSubnet: \"10.96.0.0/16\",\n});\n\nconst NAME = /^[a-z0-9]([a-z0-9-]{0,30}[a-z0-9])?$/;\n\n/** This function gives the problems of a scenario: a name that kind refuses, no image for the version, or a count out of range. */\nexport const scenarioProblems = (s: KindScenario): string[] => {\n const problems: string[] = [];\n if (!NAME.test(s.name)) problems.push(`the name ${s.name} is not lowercase letters, digits and dashes of 1 to 32 characters`);\n if (NODE_IMAGES[s.kubernetes] === undefined) problems.push(`no node image is pinned for Kubernetes ${s.kubernetes}`);\n if (!Number.isInteger(s.workers) || s.workers < 0 || s.workers > 8) problems.push(`${s.workers} workers is not a whole number from 0 to 8`);\n if (cidrOverlap(s.podSubnet, s.serviceSubnet)) problems.push(`the pod subnet ${s.podSubnet} overlaps the service subnet ${s.serviceSubnet}`);\n return problems;\n};\n\n/** This function gives the names of the node containers, as kind names them: the control plane, then each worker. */\nexport const nodeNames = (s: KindScenario): string[] => [\n `${s.name}-control-plane`,\n ...Array.from({ length: s.workers }, (_, at) => `${s.name}-worker${at === 0 ? \"\" : at + 1}`),\n];\n\n/** This function gives a duration as kube-proxy reads it, in whole milliseconds: `1s`, `500ms`. */\nexport const goDuration = (amount: Quantity): string => {\n const ms = valueIn(amount, \"ms\");\n if (!Number.isInteger(ms) || ms < 0) throw new RangeError(`${ms} ms is not a whole number of milliseconds`);\n return ms % 1000 === 0 ? `${ms / 1000}s` : `${ms}ms`;\n};\n\n/** This function gives the KubeProxyConfiguration patch of a scenario, as kubeadm reads it. */\nexport const kubeProxyPatch = (s: KindScenario): string =>\n [\n \"apiVersion: kubeproxy.config.k8s.io/v1alpha1\",\n \"kind: KubeProxyConfiguration\",\n `mode: ${s.kubeProxy.mode}`,\n `${s.kubeProxy.mode}:`,\n ` minSyncPeriod: ${goDuration(s.kubeProxy.minSyncPeriod)}`,\n ` syncPeriod: ${goDuration(s.kubeProxy.syncPeriod)}`,\n \"\",\n ].join(\"\\n\");\n\n/** A kind configuration is the cluster file that `kind create cluster --config` reads. JSON is YAML, thus the lab writes it as JSON. */\nexport interface KindConfig {\n readonly kind: \"Cluster\";\n readonly apiVersion: \"kind.x-k8s.io/v1alpha4\";\n readonly networking: {\n readonly ipFamily: \"ipv4\";\n readonly kubeProxyMode: KubeProxyMode;\n readonly disableDefaultCNI: boolean;\n readonly podSubnet: string;\n readonly serviceSubnet: string;\n };\n readonly nodes: ReadonlyArray<{\n readonly role: \"control-plane\" | \"worker\";\n readonly image: string;\n readonly labels: Readonly<Record<string, string>>;\n }>;\n readonly kubeadmConfigPatches: readonly string[];\n}\n\n/** This function generates the configuration of kind from a scenario. It refuses a scenario with a problem. */\nexport const kindConfig = (s: KindScenario): KindConfig => {\n const problems = scenarioProblems(s);\n if (problems.length > 0) throw new Error(`the scenario ${s.name}: ${problems.join(\"; \")}`);\n const image = NODE_IMAGES[s.kubernetes]!.image;\n return {\n kind: \"Cluster\",\n apiVersion: \"kind.x-k8s.io/v1alpha4\",\n networking: {\n ipFamily: \"ipv4\",\n kubeProxyMode: s.kubeProxy.mode,\n disableDefaultCNI: false,\n podSubnet: s.podSubnet,\n serviceSubnet: s.serviceSubnet,\n },\n nodes: nodeNames(s).map((node, at) => ({\n role: at === 0 ? \"control-plane\" : \"worker\",\n image,\n labels: { \"lab.systems/node\": node.slice(s.name.length + 1) },\n })),\n kubeadmConfigPatches: [kubeProxyPatch(s)],\n };\n};\n\n/** This function gives the first and the last address of an IPv4 CIDR, as numbers. */\nexport const cidrRange = (cidr: string): [number, number] => {\n const match = /^(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)\\/(\\d+)$/.exec(cidr);\n if (match === null) throw new Error(`not an IPv4 CIDR: ${cidr}`);\n const octets = match.slice(1, 5).map(Number);\n const bits = Number(match[5]);\n if (octets.some((octet) => octet > 255) || bits > 32) throw new Error(`not an IPv4 CIDR: ${cidr}`);\n const base = octets.reduce((sum, octet) => sum * 256 + octet, 0);\n const size = 2 ** (32 - bits);\n const first = Math.floor(base / size) * size;\n return [first, first + size - 1];\n};\n\n/** This function tells if two IPv4 CIDRs share an address. */\nexport const cidrOverlap = (a: string, b: string): boolean => {\n const [a0, a1] = cidrRange(a);\n const [b0, b1] = cidrRange(b);\n return a0 <= b1 && b0 <= a1;\n};\n","import { createHash } from \"node:crypto\";\nimport { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/**\n * A tool is a binary that the lab downloads, at one pinned release, for each platform. The lab\n * keeps it in the cache folder of this package and does not install it on the system. Each use\n * compares its sha256 with the pinned value.\n */\nexport interface Tool {\n readonly name: \"kind\" | \"kubectl\";\n readonly version: string;\n /** These are the release assets, by the platform and the architecture of Node: `win32-x64`, `linux-x64`. */\n readonly assets: Readonly<Record<string, { readonly url: string; readonly sha256: string }>>;\n}\n\n/** This release of kind is v0.33.0, from 2026-08-26. Its default node image is Kubernetes 1.37.0. The sums are from its `.sha256sum` assets. */\nexport const KIND: Tool = {\n name: \"kind\",\n version: \"v0.33.0\",\n assets: {\n \"win32-x64\": {\n url: \"https://github.com/kubernetes-sigs/kind/releases/download/v0.33.0/kind-windows-amd64\",\n sha256: \"4b22adaa135368c5a465d56bbd8e520cbea87272a06ca00b6078e7b81515c9fc\",\n },\n \"linux-x64\": {\n url: \"https://github.com/kubernetes-sigs/kind/releases/download/v0.33.0/kind-linux-amd64\",\n sha256: \"aee6151561422756b764a4ae28e7f44cda5af5a9eead3cc9985112b1de8d8e0d\",\n },\n \"linux-arm64\": {\n url: \"https://github.com/kubernetes-sigs/kind/releases/download/v0.33.0/kind-linux-arm64\",\n sha256: \"20022bee6cfcd5086cb7234d218e3454e6090022f2a8f55d1fa7fcf42c3867a2\",\n },\n \"darwin-arm64\": {\n url: \"https://github.com/kubernetes-sigs/kind/releases/download/v0.33.0/kind-darwin-arm64\",\n sha256: \"0c8c7dbe5e23594a198b786c4bc13dacc101fa6196b0cb0b23a1ca44e61f4b4f\",\n },\n },\n};\n\n/** This release of kubectl is v1.37.0, the version of the node image. The sums are from the `.sha256` files of `dl.k8s.io`. */\nexport const KUBECTL: Tool = {\n name: \"kubectl\",\n version: \"v1.37.0\",\n assets: {\n \"win32-x64\": {\n url: \"https://dl.k8s.io/release/v1.37.0/bin/windows/amd64/kubectl.exe\",\n sha256: \"4721b614a67bb4932a0369e61f4a323d8c6ca00943d3a2ff14837c124da06f0e\",\n },\n \"linux-x64\": {\n url: \"https://dl.k8s.io/release/v1.37.0/bin/linux/amd64/kubectl\",\n sha256: \"6129359f4e1f3848a5572ccb0b26cf28b8ca08cef38c95a765b2f64a2c961a2f\",\n },\n \"linux-arm64\": {\n url: \"https://dl.k8s.io/release/v1.37.0/bin/linux/arm64/kubectl\",\n sha256: \"922df28df248cc00a9e025f947704f1d1482de64ece54cfe57e61f19eaf1eef3\",\n },\n \"darwin-arm64\": {\n url: \"https://dl.k8s.io/release/v1.37.0/bin/darwin/arm64/kubectl\",\n sha256: \"583beedaebe422e71d3f1a96acef8b1fef86ea2f09a45ad01aa6c9ce287c1380\",\n },\n },\n};\n\n/** This folder is the cache of the tools: `.cache/` in this package, which git ignores. `KIND_CACHE` names another folder. */\nexport const cacheFolder = (): string =>\n process.env[\"KIND_CACHE\"] ?? fileURLToPath(new URL(\"../.cache/\", import.meta.url));\n\n/** This function gives the platform key of this process, as the assets name it. */\nexport const platformKey = (platform: string = process.platform, arch: string = process.arch): string =>\n `${platform}-${arch}`;\n\n/** This function gives the sha256 of some bytes, in hexadecimal. */\nexport const sha256 = (bytes: Uint8Array): string => createHash(\"sha256\").update(bytes).digest(\"hex\");\n\n/** This function gives the path of a tool in the cache, for a platform. */\nexport const toolPath = (tool: Tool, key: string = platformKey(), cache: string = cacheFolder()): string =>\n join(cache, \"tools\", `${tool.name}-${tool.version}-${key}`, key.startsWith(\"win32\") ? `${tool.name}.exe` : tool.name);\n\n/**\n * This function gives the path of a tool, after a download when the cache does not have it. It\n * refuses bytes whose sha256 is not the pinned value, from the cache or from the download.\n */\nexport const ensureTool = async (tool: Tool, key: string = platformKey(), cache: string = cacheFolder()): Promise<string> => {\n const asset = tool.assets[key];\n if (asset === undefined) throw new Error(`${tool.name} ${tool.version} has no pinned asset for ${key}`);\n const path = toolPath(tool, key, cache);\n if (existsSync(path)) {\n const found = sha256(readFileSync(path));\n if (found !== asset.sha256) throw new Error(`${path} has the sha256 ${found}, not the pinned ${asset.sha256}`);\n return path;\n }\n const response = await fetch(asset.url, { redirect: \"follow\" });\n if (!response.ok) throw new Error(`the download of ${asset.url} answered ${response.status}`);\n const bytes = new Uint8Array(await response.arrayBuffer());\n const found = sha256(bytes);\n if (found !== asset.sha256) throw new Error(`${asset.url} has the sha256 ${found}, not the pinned ${asset.sha256}`);\n mkdirSync(dirname(path), { recursive: true });\n const partial = `${path}.partial`;\n writeFileSync(partial, bytes);\n if (!key.startsWith(\"win32\")) chmodSync(partial, 0o755);\n renameSync(partial, path);\n return path;\n};\n","import { execFile } from \"node:child_process\";\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport type { ClusterManifest } from \"./record.ts\";\nimport type { KindScenario } from \"./scenario.ts\";\nimport { NODE_IMAGES, cidrOverlap, kindConfig, nodeNames } from \"./scenario.ts\";\nimport { KIND, KUBECTL, ensureTool } from \"./tools.ts\";\n\nconst run = promisify(execFile);\n\n/** This function starts a program and gives its standard output. A failure gives its standard error in the message. */\nexport const command = async (\n program: string,\n args: readonly string[],\n options: { readonly env?: Readonly<Record<string, string>>; readonly input?: string; readonly timeout?: number } = {},\n): Promise<string> => {\n try {\n const child = run(program, [...args], {\n env: { ...process.env, ...options.env },\n maxBuffer: 256 << 20,\n timeout: options.timeout ?? 600_000,\n windowsHide: true,\n });\n if (options.input !== undefined) {\n child.child.stdin?.end(options.input);\n }\n const { stdout } = await child;\n return stdout.toString();\n } catch (error) {\n const failed = error as { stderr?: string; stdout?: string; message?: string };\n throw new Error(`${program} ${args.join(\" \")} failed: ${(failed.stderr ?? failed.message ?? \"\").toString().trim().slice(0, 2000)}`);\n }\n};\n\n/** The credentials of the API, as a container on the network of the cluster reaches it. */\nexport interface Credentials {\n readonly server: string;\n readonly ca: string;\n readonly cert: string;\n readonly key: string;\n}\n\n/** A cluster is one kind cluster that the lab made, with its own kubeconfig and Docker network. */\nexport interface Cluster {\n readonly scenario: KindScenario;\n readonly dir: string;\n readonly kubeconfig: string;\n readonly network: string;\n readonly nodes: readonly string[];\n kubectl(args: readonly string[], input?: string): Promise<string>;\n /** This method applies Kubernetes objects, as JSON on standard input. */\n apply(objects: readonly unknown[]): Promise<void>;\n /** This method starts a command in a node container. */\n exec(node: string, argv: readonly string[]): Promise<string>;\n /** This method copies a local Docker image into each node. */\n load(image: string): Promise<void>;\n credentials(): Promise<Credentials>;\n manifest(): Promise<ClusterManifest>;\n delete(): Promise<void>;\n}\n\n/** This function gives the subnets of the Docker networks, by their names. */\nconst dockerSubnets = async (): Promise<Array<[string, string]>> => {\n const ids = (await command(\"docker\", [\"network\", \"ls\", \"-q\"])).split(/\\s+/).filter((id) => id !== \"\");\n if (ids.length === 0) return [];\n const info = JSON.parse(await command(\"docker\", [\"network\", \"inspect\", ...ids])) as Array<{\n Name: string;\n IPAM?: { Config?: Array<{ Subnet?: string }> | null };\n }>;\n return info.flatMap((network) =>\n (network.IPAM?.Config ?? [])\n .map((config) => config.Subnet ?? \"\")\n .filter((subnet) => /^\\d+\\.\\d+\\.\\d+\\.\\d+\\/\\d+$/.test(subnet))\n .map((subnet): [string, string] => [network.Name, subnet]),\n );\n};\n\n/**\n * This function makes a kind cluster from a scenario, in a folder that holds its config, its\n * kubeconfig and the cache of kubectl. The cluster gets a Docker network of its own. The function\n * refuses a pod or service subnet that overlaps a Docker network, because that breaks the traffic\n * between the nodes.\n */\nexport const createCluster = async (scenario: KindScenario, dir: string): Promise<Cluster> => {\n const kind = await ensureTool(KIND);\n const kubectlPath = await ensureTool(KUBECTL);\n mkdirSync(dir, { recursive: true });\n const kubeconfig = join(dir, \"kubeconfig\");\n const network = `systems-${scenario.name}`;\n const config = kindConfig(scenario);\n for (const [name, subnet] of await dockerSubnets()) {\n if (name === network) continue;\n for (const ours of [scenario.podSubnet, scenario.serviceSubnet])\n if (cidrOverlap(ours, subnet)) throw new Error(`the subnet ${ours} overlaps the Docker network ${name} (${subnet})`);\n }\n const file = join(dir, \"kind.json\");\n writeFileSync(file, `${JSON.stringify(config, null, 2)}\\n`);\n const env = { KIND_EXPERIMENTAL_DOCKER_NETWORK: network };\n const nodes = nodeNames(scenario);\n const kubectl = (args: readonly string[], input?: string) =>\n command(kubectlPath, [\"--kubeconfig\", kubeconfig, \"--cache-dir\", join(dir, \"kubectl-cache\"), ...args], input === undefined ? {} : { input });\n const cluster: Cluster = {\n scenario,\n dir,\n kubeconfig,\n network,\n nodes,\n kubectl,\n async apply(objects) {\n await kubectl([\"apply\", \"-f\", \"-\"], JSON.stringify({ apiVersion: \"v1\", kind: \"List\", items: objects }));\n },\n exec: (node, argv) => command(\"docker\", [\"exec\", node, ...argv]),\n async load(image) {\n await command(kind, [\"load\", \"docker-image\", \"--name\", scenario.name, image], { env, timeout: 900_000 });\n },\n async credentials() {\n const internal = join(dir, \"kubeconfig.internal\");\n writeFileSync(internal, await command(kind, [\"get\", \"kubeconfig\", \"--internal\", \"--name\", scenario.name], { env }));\n const view = JSON.parse(\n await command(kubectlPath, [\"--kubeconfig\", internal, \"config\", \"view\", \"--raw\", \"-o\", \"json\"]),\n ) as {\n clusters: Array<{ cluster: { server: string; \"certificate-authority-data\": string } }>;\n users: Array<{ user: { \"client-certificate-data\": string; \"client-key-data\": string } }>;\n };\n return {\n server: view.clusters[0]!.cluster.server,\n ca: view.clusters[0]!.cluster[\"certificate-authority-data\"],\n cert: view.users[0]!.user[\"client-certificate-data\"],\n key: view.users[0]!.user[\"client-key-data\"],\n };\n },\n async manifest() {\n const nodesJson = JSON.parse(await kubectl([\"get\", \"nodes\", \"-o\", \"json\"])) as {\n items: Array<{ metadata: { name: string }; status: { nodeInfo: Record<string, string> } }>;\n };\n const proxy = JSON.parse(await kubectl([\"-n\", \"kube-system\", \"get\", \"configmap\", \"kube-proxy\", \"-o\", \"json\"])) as {\n data: Record<string, string>;\n };\n const mode = /^mode:\\s*(\\S*)/m.exec(proxy.data[\"config.conf\"] ?? \"\")?.[1] ?? \"\";\n const version = JSON.parse(await kubectl([\"version\", \"-o\", \"json\"])) as { serverVersion?: { gitVersion?: string } };\n return {\n kind: (await command(kind, [\"version\"])).trim(),\n nodeImage: NODE_IMAGES[scenario.kubernetes]!.image,\n server: version.serverVersion?.gitVersion ?? \"\",\n mode: mode.replaceAll('\"', \"\"),\n docker: (await command(\"docker\", [\"version\", \"--format\", \"{{.Server.Version}}\"])).trim(),\n nodes: nodesJson.items.map((item) => ({\n name: item.metadata.name,\n kubelet: item.status.nodeInfo[\"kubeletVersion\"] ?? \"\",\n kernel: item.status.nodeInfo[\"kernelVersion\"] ?? \"\",\n runtime: item.status.nodeInfo[\"containerRuntimeVersion\"] ?? \"\",\n os: item.status.nodeInfo[\"osImage\"] ?? \"\",\n })),\n };\n },\n async delete() {\n await command(kind, [\"delete\", \"cluster\", \"--name\", scenario.name, \"--kubeconfig\", kubeconfig], { env });\n await command(\"docker\", [\"network\", \"rm\", network]).catch(() => undefined);\n },\n };\n await command(\n kind,\n [\"create\", \"cluster\", \"--name\", scenario.name, \"--config\", file, \"--kubeconfig\", kubeconfig, \"--wait\", \"180s\"],\n { env, timeout: 900_000 },\n );\n return cluster;\n};\n","// The composition of the service of a kind cell. The app of the image and `lint()` compose the same\n// service from the same declaration: the image copies this file, and Node strips its types. Thus\n// this file imports only packages and types.\nimport type { CheckFn, HealthServer, ServiceBuilder, TransportImpl } from \"@mark1russell7/service\";\nimport { service } from \"@mark1russell7/service\";\nimport type { RedisClient } from \"@mark1russell7/service/redis\";\nimport type { Declaration } from \"./cell.ts\";\n\n/** These are the parts of a running service. A composition for `lint()` gives none of them. */\nexport interface Parts {\n readonly redis?: RedisClient | undefined;\n readonly flag?: CheckFn | undefined;\n readonly http?: TransportImpl | undefined;\n readonly health?: HealthServer | undefined;\n}\n\n/** This name is the controller of the service, and its one route reads Redis when the service has Redis. */\nexport const CONTROLLER = \"work\";\nexport const ROUTE = \"GET /work\";\n/** This name is the check that a flag file fails: the seam of the liveness restart. */\nexport const FLAG_CHECK = \"self.flag\";\n/** This name is the watchdog of the event loop. */\nexport const WATCHDOG = \"self.event-loop\";\n\n/**\n * This function composes the service of a declaration: its policies, its couplings, its checks,\n * one server, one controller and each stage of the shutdown. A declaration with Redis needs the\n * client, because the client declares the dependency and its checks.\n */\nexport const compose = (name: string, declaration: Declaration, parts: Parts = {}): ServiceBuilder => {\n const svc = service(name)\n .readiness(declaration.readiness)\n .liveness(declaration.liveness)\n .startup(declaration.startup)\n .cooperativeWindow(declaration.cooperativeWindow);\n for (const coupling of declaration.couplings) svc.couple(coupling);\n if (declaration.redis !== null) {\n if (parts.redis === undefined) throw new Error(`${name} declares Redis, thus its composition needs the client`);\n parts.redis.attach(svc, { poll: declaration.poll, steps: { \"round-trip\": declaration.redis.gates } });\n }\n if (declaration.flag !== null)\n svc.check(\n {\n name: FLAG_CHECK,\n dependency: null,\n step: \"local-state\",\n ownConnection: true,\n gates: declaration.flag,\n poll: declaration.poll,\n },\n parts.flag,\n );\n if (declaration.watchdog !== null)\n svc.watchdog({\n name: WATCHDOG,\n tick: declaration.watchdog.tick,\n threshold: declaration.watchdog.threshold,\n gates: \"liveness\",\n poll: declaration.poll,\n });\n svc.server(\"http\", {}, parts.http);\n const controller = svc.controller(CONTROLLER);\n if (declaration.redis !== null) controller.requires(parts.redis!.name);\n controller.route(ROUTE, { idempotent: true });\n if (parts.health !== undefined) svc.healthServer(parts.health);\n for (const [stage, cap] of declaration.phases) svc.phase(stage, cap);\n return svc;\n};\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport type { KindCell } from \"./cell.ts\";\nimport { CLIENT, REDIS, SERVER } from \"./names.ts\";\n\n/**\n * This module maps each trigger of a cell to the acts of the lab, as data. The runner finds each\n * target at the time of the run. A target is a pod, the PID of a container, the network namespace\n * of a pod, or a cgroup.\n */\n\n/**\n * A target is a process or a pod of the cell:\n * - `server` is the first replica of the service, in the sequence of its name.\n * - `redis` is the Redis pod.\n * - `kube-proxy` is kube-proxy on the first worker.\n */\nexport type Target = \"server\" | \"redis\" | \"kube-proxy\";\n\n/** A step is one act of a fault, in the sequence of the plan. */\nexport type Step =\n | { readonly do: \"kubectl\"; readonly args: readonly string[]; readonly background?: boolean }\n | { readonly do: \"signal\"; readonly target: Target; readonly signal: \"SIGKILL\" | \"SIGSTOP\" | \"SIGCONT\" }\n /** This step sends SIGSTOP, then SIGCONT `time` later, as the VM times it. The next step starts at once. */\n | { readonly do: \"pause\"; readonly target: Target; readonly time: Quantity }\n /** This step starts the injector of the harness in the namespace of the target, with each replica of the service as a peer. */\n | { readonly do: \"inject\"; readonly target: Target; readonly action: \"blackhole\" | \"clear\" | \"rst\" | \"clear-rst\" }\n | { readonly do: \"flag\"; readonly target: Target; readonly on: boolean }\n | { readonly do: \"cpu\"; readonly target: Target; readonly max: string }\n /** This step blocks the event loop of the target for `time`. */\n | { readonly do: \"stall\"; readonly target: Target; readonly time: Quantity }\n | { readonly do: \"wait\"; readonly time: Quantity };\n\n/**\n * A fault plan is the steps of a run:\n * - `inject` starts the trigger. The stamp of its first act is the `fault` reference.\n * - `heal` ends the fault, `hold` after the fault. A trigger that ends by itself has no heal.\n * - `restore` brings the cell back to its steady state after the run.\n */\nexport interface FaultPlan {\n readonly inject: readonly Step[];\n readonly hold: Quantity | null;\n readonly heal: readonly Step[];\n readonly restore: readonly Step[];\n}\n\nconst needs = (time: Quantity | undefined, what: string): Quantity => {\n if (time === undefined) throw new Error(`the trigger needs ${what}`);\n return time;\n};\n\n/** This function gives an amount in microseconds as a whole number, for `cpu.max`. */\nconst micros = (amount: Quantity): number => Math.round(valueIn(amount, \"us\"));\n\n/** This value marks the name of the target pod in the arguments of kubectl. The runner replaces it. */\nexport const POD = \"{pod}\";\n/** This value marks the node of the target pod in the arguments of kubectl. */\nexport const NODE = \"{node}\";\n/** This value marks the next version of the Deployment, for a rollout. */\nexport const VERSION = \"{version}\";\n\n/** This function gives the fault plan of a cell. */\nexport const planOf = (cell: KindCell): FaultPlan => {\n const ns = [\"-n\", \"{namespace}\"];\n const trigger = cell.trigger;\n const replicas = cell.pod.replicas;\n switch (trigger.kind) {\n case \"pod-deletion\":\n return { inject: [{ do: \"kubectl\", args: [...ns, \"delete\", \"pod\", POD, \"--wait=false\"] }], hold: null, heal: [], restore: [] };\n case \"rolling-update\":\n return {\n inject: [{ do: \"kubectl\", args: [...ns, \"set\", \"env\", `deployment/${SERVER}`, `LAB_VERSION=${VERSION}`] }],\n hold: null,\n heal: [],\n restore: [],\n };\n case \"node-drain\":\n return {\n inject: [\n {\n do: \"kubectl\",\n args: [\"drain\", NODE, \"--ignore-daemonsets\", \"--delete-emptydir-data\", \"--timeout=180s\", `--pod-selector=app=${SERVER}`],\n background: true,\n },\n ],\n hold: null,\n heal: [],\n restore: [{ do: \"kubectl\", args: [\"uncordon\", NODE] }],\n };\n case \"scale-in\":\n return {\n inject: [{ do: \"kubectl\", args: [...ns, \"scale\", `deployment/${SERVER}`, `--replicas=${replicas - 1}`] }],\n hold: null,\n heal: [],\n restore: [{ do: \"kubectl\", args: [...ns, \"scale\", `deployment/${SERVER}`, `--replicas=${replicas}`] }],\n };\n case \"crash\":\n return { inject: [{ do: \"signal\", target: \"server\", signal: \"SIGKILL\" }], hold: null, heal: [], restore: [] };\n case \"liveness-restart\":\n // the container that the kubelet starts again has a new /tmp, thus the flag ends with it\n return { inject: [{ do: \"flag\", target: \"server\", on: true }], hold: null, heal: [], restore: [] };\n case \"freeze-process\":\n if (cell.template === \"T07\") {\n return {\n inject: [\n { do: \"pause\", target: \"kube-proxy\", time: needs(trigger.hold, \"a hold\") },\n { do: \"wait\", time: needs(trigger.lead, \"a lead\") },\n { do: \"kubectl\", args: [...ns, \"delete\", \"pod\", POD, \"--wait=false\"] },\n ],\n hold: null,\n heal: [],\n restore: [],\n };\n }\n return {\n inject: [{ do: \"signal\", target: \"redis\", signal: \"SIGSTOP\" }],\n hold: needs(trigger.hold, \"a hold\"),\n heal: [{ do: \"signal\", target: \"redis\", signal: \"SIGCONT\" }],\n restore: [],\n };\n case \"rst\":\n return {\n inject: [{ do: \"inject\", target: \"redis\", action: \"rst\" }],\n hold: needs(trigger.hold, \"a hold\"),\n heal: [{ do: \"inject\", target: \"redis\", action: \"clear-rst\" }],\n restore: [],\n };\n case \"blackhole\":\n return {\n inject: [{ do: \"inject\", target: \"redis\", action: \"blackhole\" }],\n hold: needs(trigger.hold, \"a hold\"),\n heal: [{ do: \"inject\", target: \"redis\", action: \"clear\" }],\n restore: [],\n };\n case \"peer-gone-without-close\":\n // the rules drop the FIN of Redis, then Redis goes away with its namespace and its rules\n return {\n inject: [\n { do: \"inject\", target: \"redis\", action: \"blackhole\" },\n { do: \"kubectl\", args: [...ns, \"scale\", `deployment/${REDIS}`, \"--replicas=0\"] },\n ],\n hold: needs(trigger.hold, \"a hold\"),\n heal: [{ do: \"kubectl\", args: [...ns, \"scale\", `deployment/${REDIS}`, \"--replicas=1\"] }],\n restore: [],\n };\n case \"app-stall\":\n return { inject: [{ do: \"stall\", target: \"server\", time: needs(trigger.hold, \"a hold\") }], hold: null, heal: [], restore: [] };\n case \"rollout\":\n // Redis is down for each pod, a new one too: the Service of Redis has no endpoint\n return {\n inject: [\n { do: \"kubectl\", args: [...ns, \"scale\", `deployment/${REDIS}`, \"--replicas=0\"] },\n { do: \"kubectl\", args: [...ns, \"set\", \"env\", `deployment/${SERVER}`, `LAB_VERSION=${VERSION}`] },\n ],\n hold: needs(trigger.hold, \"a hold\"),\n heal: [{ do: \"kubectl\", args: [...ns, \"scale\", `deployment/${REDIS}`, \"--replicas=1\"] }],\n restore: [],\n };\n case \"cpu-throttle\": {\n const max = trigger.cpuMax;\n if (max === undefined) throw new Error(\"the trigger needs its cpu.max\");\n // the kernel reads cpu.max as the quota and the period in microseconds\n return {\n inject: [{ do: \"cpu\", target: \"server\", max: `${micros(max.quota)} ${micros(max.period)}` }],\n hold: needs(trigger.hold, \"a hold\"),\n heal: [{ do: \"cpu\", target: \"server\", max: `max ${micros(max.period)}` }],\n restore: [],\n };\n }\n }\n};\n\n/** This function tells if a trigger stops a pod of the service with its grace: a deletion, an eviction, a scale-in or a rollout. */\nexport const deletes = (cell: KindCell): boolean =>\n [\"pod-deletion\", \"rolling-update\", \"node-drain\", \"scale-in\"].includes(cell.trigger.kind) ||\n (cell.template === \"T07\" && cell.trigger.kind === \"freeze-process\");\n\n/** These are the names of the objects of a cell that a plan names. */\nexport const OBJECTS: { readonly server: typeof SERVER; readonly client: typeof CLIENT; readonly redis: typeof REDIS } = {\n server: SERVER,\n client: CLIENT,\n redis: REDIS,\n};\n","import { execFileSync } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, realpathSync, rmSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/** This folder is the root of the package, and its `image/` folder has the files of the image. */\nconst PACKAGE = fileURLToPath(new URL(\"../\", import.meta.url));\n/** This folder is the root of the repository, when the package is in the workspace of the repository. */\nconst REPO = fileURLToPath(new URL(\"../../../\", import.meta.url));\n/** These are the packages of this repository that the image holds, in the sequence of their dependencies. */\nexport const WORKSPACE_PACKAGES: readonly [\"quantity\", \"algebra\", \"service\"] = [\"quantity\", \"algebra\", \"service\"];\n/** These are the files of the image, by their name in the context and their source in the package. */\nconst FILES: ReadonlyArray<readonly [string, string]> = [\n [\"Dockerfile\", \"image/Dockerfile\"],\n [\"package.json\", \"image/package.json\"],\n [\"npm-lock.json\", \"image/npm-lock.json\"],\n [\"app.mjs\", \"image/app.mjs\"],\n [\"client.mjs\", \"image/client.mjs\"],\n [\"agent.mjs\", \"image/agent.mjs\"],\n [\"watch.mjs\", \"image/watch.mjs\"],\n [\"compose.ts\", \"src/compose.ts\"],\n [\"backends.ts\", \"src/backends.ts\"],\n];\n\nconst shell = process.platform === \"win32\";\n\n/** This function gives each file of a staged context, with its path in the context, in a stable sequence. */\nconst filesOf = (root: string, at = \"\"): string[] =>\n readdirSync(join(root, at), { withFileTypes: true })\n .flatMap((entry) => (entry.isDirectory() ? filesOf(root, join(at, entry.name)) : [join(at, entry.name)]))\n .sort();\n\n/** This function gives the tag of a staged context from its content: the name, then 12 digits of a sha256. */\nexport const contentTag = (name: string, root: string): string => {\n const hash = createHash(\"sha256\");\n for (const file of filesOf(root)) {\n hash.update(file.replaceAll(\"\\\\\", \"/\"));\n hash.update(readFileSync(join(root, file)));\n }\n return `${name}:${hash.digest(\"hex\").slice(0, 12)}`;\n};\n\n/** This function tells if the package is in the workspace of its repository. From npm, it is not. */\nconst inWorkspace = (): boolean =>\n existsSync(join(REPO, \"pnpm-workspace.yaml\")) && WORKSPACE_PACKAGES.every((name) => existsSync(join(REPO, \"packages\", name, \"package.json\")));\n\n/**\n * This function gives the folder of each installed package that the image holds. Node finds a\n * package from the folder of a package that depends on it. The service depends on the algebra,\n * thus the search starts with the service.\n */\nconst installedFolders = (): Map<string, string> => {\n const folders = new Map<string, string>();\n for (const name of [...WORKSPACE_PACKAGES].reverse()) {\n const found = [PACKAGE, ...folders.values()].flatMap((from) => {\n try {\n return [dirname(realpathSync(createRequire(join(from, \"package.json\")).resolve(`@mark1russell7/${name}/package.json`)))];\n } catch {\n return [];\n }\n })[0];\n if (found === undefined) throw new Error(`@mark1russell7/${name} is not installed with @mark1russell7/kind`);\n folders.set(name, found);\n }\n return folders;\n};\n\n/**\n * This function stages the build context of the lab image. In the workspace, it builds the packages\n * of the repository and packs each one as npm gets it. From npm, it packs the installed packages.\n * Then it copies the files of the image.\n */\nexport const stageLabImage = (): string => {\n const context = mkdtempSync(join(tmpdir(), \"systems-kind-lab-\"));\n const packed = join(context, \"workspace\");\n mkdirSync(packed);\n if (inWorkspace()) {\n const filters = WORKSPACE_PACKAGES.flatMap((name) => [\"--filter\", `@mark1russell7/${name}`]);\n execFileSync(\"pnpm\", [...filters, \"build\"], { cwd: REPO, shell, stdio: \"ignore\" });\n for (const name of WORKSPACE_PACKAGES)\n execFileSync(\"pnpm\", [\"pack\", \"--pack-destination\", packed], { cwd: join(REPO, \"packages\", name), shell, stdio: \"ignore\" });\n } else {\n for (const folder of installedFolders().values())\n execFileSync(\"npm\", [\"pack\", \"--pack-destination\", packed, \"--ignore-scripts\"], { cwd: folder, shell, stdio: \"ignore\" });\n }\n for (const [at, from] of FILES) copyFileSync(join(PACKAGE, from), join(context, at));\n return context;\n};\n\nlet built: Promise<string> | null = null;\n\n/** This function builds the lab image one time for each process, and gives its tag. Docker keeps its layers between builds. */\nexport const labImage = (): Promise<string> => {\n built ??= (async () => {\n const context = stageLabImage();\n try {\n const tag = contentTag(\"systems-kind-lab\", context);\n const { GenericContainer } = await import(\"testcontainers\");\n await GenericContainer.fromDockerfile(context).build(tag, { deleteOnExit: false });\n return tag;\n } finally {\n rmSync(context, { recursive: true, force: true });\n }\n })();\n return built;\n};\n","import type { StartedTestContainer } from \"testcontainers\";\nimport { harnessImage } from \"@mark1russell7/harness\";\nimport type { Quantity } from \"@mark1russell7/quantity\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport type { ServiceRule } from \"./backends.ts\";\nimport type { Cluster } from \"./cluster.ts\";\nimport { command } from \"./cluster.ts\";\nimport type { Line } from \"./observe.ts\";\n\nexport type { Line } from \"./observe.ts\";\n\n/** A stamp brackets an act on CLOCK_MONOTONIC, in milliseconds. */\nexport interface Stamp {\n readonly t0: number;\n readonly t1: number;\n}\n\n/** These kinds of kernel record stay in the raw record of a run. The lab counts the other kinds and drops them. */\nexport const KEPT_KERNEL: ReadonlySet<string> = new Set([\"rst_tx\", \"rst_rx\", \"rst_v4\", \"error\", \"retrans\", \"begin\", \"fail\"]);\n\n/** These are the sidecars of one node: the agent of the lab and the injector of the harness, in the PID namespace of the node. */\ninterface Sidecars {\n readonly agent: StartedTestContainer;\n readonly injector: StartedTestContainer;\n}\n\n/** A kind lab is the instruments of one cluster: a sidecar pair on each node, the watcher of the API and the prober. */\nexport interface KindLab {\n readonly cluster: Cluster;\n readonly image: string;\n /** These are the lines since the last `take()`. */\n readonly lines: Line[];\n /** This record counts the kernel records that the lab dropped, by kind. */\n readonly dropped: Record<string, number>;\n /** This method gives the names of the containers that the lab started: they are not the lab containers of another agent. */\n own(): string[];\n /** This method gives the lines since the last call, and starts a new buffer. */\n take(): Line[];\n /** This method adds a listener of each line, and gives the function that removes it. */\n listen(listener: (line: Line) => void): () => void;\n watch(namespaces: readonly string[]): Promise<void>;\n probe(netns: readonly number[]): Promise<void>;\n stopProbe(): Promise<void>;\n /** This method asks each agent to poll the backends of a Service in the rules of its node, `every` apart, or to stop with null. */\n rules(rule: (ServiceRule & { readonly every: Quantity }) | null): Promise<void>;\n act(node: string, spec: Readonly<Record<string, unknown>>): Promise<Stamp>;\n inject(node: string, pid: number, args: readonly string[]): Promise<Stamp>;\n now(): Promise<number>;\n pidOf(node: string, namespace: string, pod: string, container: string): Promise<number>;\n netnsOf(node: string, pid: number): Promise<number>;\n /** This method gives the path of the cgroup of a process, as the agent reaches it through the root of the node. */\n cgroupOf(node: string, pid: number): Promise<string>;\n close(): Promise<void>;\n}\n\n/** This container class can share the process namespace of another container. */\nconst shared = async () => {\n const { GenericContainer } = await import(\"testcontainers\");\n return class extends GenericContainer {\n withPidMode(mode: string): this {\n (this as unknown as { hostConfig: { PidMode?: string } }).hostConfig.PidMode = mode;\n return this;\n }\n };\n};\n\n/** This function collects the JSON lines of a stream, and gives each one to `take`. */\nconst consume = (take: (line: Record<string, unknown>) => void) => (stream: NodeJS.ReadableStream) => {\n let partial = \"\";\n stream.on(\"data\", (chunk: Buffer | string) => {\n partial += chunk.toString();\n const split = partial.split(\"\\n\");\n partial = split.pop() ?? \"\";\n for (const text of split) {\n const trimmed = text.trim();\n if (!trimmed.startsWith(\"{\")) continue;\n try {\n take(JSON.parse(trimmed) as Record<string, unknown>);\n } catch {\n // a line that is not whole JSON is not a record\n }\n }\n });\n};\n\n/** This function gives a stamp of the injector of the harness, in nanoseconds, as milliseconds. */\nconst injectorStamp = (stdout: string): Stamp => {\n const stamp = JSON.parse(stdout.trim().split(\"\\n\").at(-1)!) as { t0: number | string; t1: number | string };\n return { t0: Number(BigInt(stamp.t0)) / 1e6, t1: Number(BigInt(stamp.t1)) / 1e6 };\n};\n\nconst output = async (container: StartedTestContainer, argv: readonly string[]): Promise<string> => {\n const result = await container.exec([...argv]);\n if (result.exitCode !== 0) throw new Error(`${argv.join(\" \")} exited ${result.exitCode}: ${result.output.trim().slice(0, 500)}`);\n return result.stdout;\n};\n\n/**\n * This function opens the instruments of a cluster. Each node gets the agent of the lab and the\n * injector of the harness, privileged and in the PID namespace of the node. Thus the agent signals\n * a process of a pod by its PID on the node. The injector enters the network namespace of a pod\n * with `nsenter`.\n */\nexport const openKindLab = async (cluster: Cluster, image: string): Promise<KindLab> => {\n const harness = await harnessImage();\n const Shared = await shared();\n const { GenericContainer } = await import(\"testcontainers\");\n let lines: Line[] = [];\n const dropped: Record<string, number> = {};\n const listeners = new Set<(line: Line) => void>();\n const push = (line: Line): void => {\n lines.push(line);\n for (const listener of listeners) listener(line);\n };\n const sidecars = new Map<string, Sidecars>();\n const opened: StartedTestContainer[] = [];\n let watcher: StartedTestContainer | null = null;\n let prober: StartedTestContainer | null = null;\n try {\n for (const node of cluster.nodes) {\n const agent = await new Shared(image)\n .withPidMode(`container:${node}`)\n .withPrivilegedMode()\n .withNetworkMode(\"none\")\n .withEnvironment({ NODE_NAME: node })\n .withCommand([\"node\", \"agent.mjs\", \"serve\"])\n .withLogConsumer(consume((line) => push({ ...(line as unknown as Line), node })))\n .start();\n opened.push(agent);\n const injector = await new Shared(harness)\n .withPidMode(`container:${node}`)\n .withPrivilegedMode()\n .withNetworkMode(\"none\")\n .start();\n opened.push(injector);\n sidecars.set(node, { agent, injector });\n }\n } catch (error) {\n for (const container of opened.reverse()) await container.stop({ timeout: 0 }).catch(() => undefined);\n throw error;\n }\n const sidecarOf = (node: string): Sidecars => {\n const found = sidecars.get(node);\n if (found === undefined) throw new Error(`the cluster has no node ${node}`);\n return found;\n };\n const firstNode = cluster.nodes[0]!;\n const lab: KindLab = {\n cluster,\n image,\n get lines() {\n return lines;\n },\n dropped,\n own() {\n return [...opened, ...(watcher === null ? [] : [watcher]), ...(prober === null ? [] : [prober])].map((container) => container.getName().replace(/^\\//, \"\"));\n },\n take() {\n const out = lines;\n lines = [];\n return out;\n },\n listen(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n async watch(namespaces) {\n if (watcher !== null) await watcher.stop({ timeout: 0 }).catch(() => undefined);\n const kube = await cluster.credentials();\n watcher = await new GenericContainer(image)\n .withNetworkMode(cluster.network)\n .withEnvironment({ KUBE: JSON.stringify(kube), NAMESPACES: namespaces.join(\",\") })\n .withCommand([\"node\", \"watch.mjs\"])\n .withLogConsumer(consume((line) => push(line as unknown as Line)))\n .start();\n },\n async probe(netns) {\n await lab.stopProbe();\n let begun: (ok: boolean, why: string) => void = () => {};\n const ready = new Promise<void>((resolve, reject) => {\n begun = (ok, why) => (ok ? resolve() : reject(new Error(`the prober failed: ${why}`)));\n });\n prober = await new GenericContainer(harness)\n .withPrivilegedMode()\n .withNetworkMode(\"none\")\n .withCommand([\"/opt/harness/prober\", ...netns.map(String)])\n .withLogConsumer(\n consume((record) => {\n const ev = String(record[\"ev\"]);\n if (ev === \"begin\") begun(true, \"\");\n if (ev === \"fail\") begun(false, String(record[\"why\"]));\n if (!KEPT_KERNEL.has(ev) && ev !== \"clock\") {\n dropped[ev] = (dropped[ev] ?? 0) + 1;\n return;\n }\n const mono = Number(record[\"mono\"] ?? 0);\n push({ ...(record as unknown as Line), ev: `kernel.${ev}`, t: mono / 1e6 });\n }),\n )\n .start();\n await Promise.race([\n ready,\n new Promise((_, reject) => setTimeout(() => reject(new Error(\"no begin line from the prober in 60 s\")), 60_000)),\n ]);\n },\n async stopProbe() {\n if (prober !== null) await prober.stop({ timeout: 0 }).catch(() => undefined);\n prober = null;\n },\n async rules(rule) {\n // the agent reads the interval of its poll in milliseconds\n const file = rule === null ? null : JSON.stringify({ ...rule, every: valueIn(rule.every, \"ms\") });\n for (const { agent } of sidecars.values()) {\n if (file === null) await output(agent, [\"rm\", \"-f\", \"/tmp/rules.json\"]);\n else await output(agent, [\"sh\", \"-c\", `printf '%s' '${file}' > /tmp/rules.json`]);\n }\n },\n async act(node, spec) {\n const stdout = await output(sidecarOf(node).agent, [\"node\", \"agent.mjs\", \"act\", JSON.stringify(spec)]);\n const result = JSON.parse(stdout.trim().split(\"\\n\").at(-1)!) as { ok: boolean; t0: number; t1: number; why?: string };\n if (!result.ok) throw new Error(`the act ${JSON.stringify(spec)} on ${node} failed: ${result.why}`);\n push({ ev: \"act\", t: result.t0, t1: result.t1, node, spec });\n return { t0: result.t0, t1: result.t1 };\n },\n async inject(node, pid, args) {\n const stdout = await output(sidecarOf(node).injector, [\"nsenter\", \"-t\", String(pid), \"-n\", \"/opt/harness/inject\", ...args]);\n const stamp = injectorStamp(stdout);\n push({ ev: \"act\", t: stamp.t0, t1: stamp.t1, node, spec: { op: \"inject\", pid, args } });\n return stamp;\n },\n async now() {\n return (await lab.act(firstNode, { op: \"now\" })).t0;\n },\n async pidOf(node, namespace, pod, container) {\n const ids = (\n await cluster.exec(node, [\n \"crictl\",\n \"ps\",\n \"-q\",\n \"--state\",\n \"running\",\n \"--label\",\n `io.kubernetes.pod.namespace=${namespace}`,\n \"--label\",\n `io.kubernetes.pod.name=${pod}`,\n \"--name\",\n `^${container}$`,\n ])\n )\n .split(/\\s+/)\n .filter((id) => id !== \"\");\n if (ids.length !== 1) throw new Error(`${namespace}/${pod}/${container} on ${node} has ${ids.length} running containers, not 1`);\n const info = JSON.parse(await cluster.exec(node, [\"crictl\", \"inspect\", ids[0]!])) as { info?: { pid?: number } };\n const pid = info.info?.pid;\n if (pid === undefined || !(pid > 1)) throw new Error(`crictl gives no PID for ${namespace}/${pod}/${container}`);\n return pid;\n },\n async netnsOf(node, pid) {\n const link = (await cluster.exec(node, [\"readlink\", `/proc/${pid}/ns/net`])).trim();\n const inode = /^net:\\[(\\d+)\\]$/.exec(link)?.[1];\n if (inode === undefined) throw new Error(`not a network namespace: ${link}`);\n return Number(inode);\n },\n async cgroupOf(node, pid) {\n const text = (await cluster.exec(node, [\"cat\", `/proc/${pid}/cgroup`])).trim();\n const path = /^0::(\\S+)$/m.exec(text)?.[1];\n // an empty or a short path is the root of the node: a write there throttles or freezes each pod of the node\n if (path === undefined || !/\\/cri-containerd-[0-9a-f]{64}\\.scope$/.test(path))\n throw new Error(`the cgroup of ${pid} on ${node} is not the scope of one container: ${text}`);\n return `/proc/1/root/sys/fs/cgroup${path}`;\n },\n async close() {\n await lab.stopProbe();\n if (watcher !== null) await watcher.stop({ timeout: 0 }).catch(() => undefined);\n watcher = null;\n for (const container of opened.reverse()) await container.stop({ timeout: 0 }).catch(() => undefined);\n },\n };\n return lab;\n};\n\n/** This function pins a Docker image by digest under a local tag that holds the digest, for `kind load` and `imagePullPolicy: Never`. */\nexport const localPin = async (image: string): Promise<string> => {\n const match = /^([^@]+?)(?::([^@:/]+))?@sha256:([0-9a-f]{64})$/.exec(image);\n if (match === null) throw new Error(`${image} is not pinned by a digest`);\n const [, repository, tag, digest] = match;\n const local = `systems-lab/${repository!.split(\"/\").at(-1)}:${tag ?? \"pinned\"}-${digest!.slice(0, 12)}`;\n const present = await command(\"docker\", [\"image\", \"inspect\", \"--format\", \"{{json .RepoDigests}}\", `${repository}@sha256:${digest}`]).catch(() => \"\");\n if (!present.includes(digest!)) await command(\"docker\", [\"pull\", `${repository}@sha256:${digest}`], { timeout: 900_000 });\n await command(\"docker\", [\"tag\", `${repository}@sha256:${digest}`, local]);\n return local;\n};\n","import { createHash } from \"node:crypto\";\nimport type { Quantity } from \"@mark1russell7/quantity\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport type { KindCell, KubeletProbe } from \"./cell.ts\";\nimport { CLIENT, HEALTH_PORT, LOG_LABEL, PORT, REDIS, SERVER } from \"./names.ts\";\n\n/**\n * This module generates the Kubernetes objects of a cell. They are its namespace, its Redis, the\n * service as a Deployment behind a Service, and one client on each node. Each setting of\n * the pod comes from the cell. The rollout strategy and the probe fields state the defaults of\n * Kubernetes as values.\n */\n\nexport { CLIENT, HEALTH_PORT, LOG_LABEL, PORT, REDIS, SERVER } from \"./names.ts\";\n\n/** This image is Redis 7.4, the stock target of the harness, pinned by digest. */\nexport const REDIS_IMAGE = \"redis:7.4@sha256:c6eabf748fc7a61dbb5a705c78bcf3d6377b1127a97d0ce965c11c44ba46896f\";\n\nconst whole = (amount: Quantity, what: string): number => {\n const seconds = valueIn(amount, \"s\");\n if (!Number.isInteger(seconds) || seconds < 0) throw new RangeError(`${what} is ${seconds} s, not a whole number of seconds`);\n return seconds;\n};\n\n/** This function gives the namespace of a cell: `lab-` and 10 digits of the sha256 of its name. */\nexport const namespaceOf = (cell: Pick<KindCell, \"name\">): string =>\n `lab-${createHash(\"sha256\").update(cell.name).digest(\"hex\").slice(0, 10)}`;\n\n/** This function gives a probe of the pod: an HTTP GET of the health server, with each field of the cell. */\nexport const probeOf = (path: string, probe: KubeletProbe): Record<string, unknown> => ({\n httpGet: { path, port: HEALTH_PORT },\n initialDelaySeconds: 0,\n periodSeconds: whole(probe.period, `the period of ${path}`),\n timeoutSeconds: whole(probe.timeout, `the timeout of ${path}`),\n failureThreshold: probe.threshold,\n successThreshold: 1,\n});\n\n/** This function gives the environment of the app: its config as JSON, and the name of its pod. */\nconst appEnv = (cell: KindCell): Array<Record<string, unknown>> => [\n { name: \"CONFIG\", value: JSON.stringify({ name: SERVER, port: PORT, healthPort: HEALTH_PORT, declaration: cell.declaration }) },\n { name: \"POD_NAME\", valueFrom: { fieldRef: { fieldPath: \"metadata.name\" } } },\n];\n\n/** These are the objects of Redis: one replica behind a Service, with no persistence. */\nexport const redisObjects = (namespace: string, image: string): unknown[] => [\n {\n apiVersion: \"apps/v1\",\n kind: \"Deployment\",\n metadata: { name: REDIS, namespace, labels: { app: REDIS } },\n spec: {\n replicas: 1,\n selector: { matchLabels: { app: REDIS } },\n template: {\n metadata: { labels: { app: REDIS } },\n spec: {\n terminationGracePeriodSeconds: 1,\n containers: [\n {\n name: REDIS,\n image,\n imagePullPolicy: \"Never\",\n command: [\"redis-server\", \"--save\", \"\", \"--appendonly\", \"no\"],\n ports: [{ containerPort: 6379 }],\n readinessProbe: { tcpSocket: { port: 6379 }, periodSeconds: 1, failureThreshold: 1, successThreshold: 1, timeoutSeconds: 1 },\n },\n ],\n },\n },\n },\n },\n {\n apiVersion: \"v1\",\n kind: \"Service\",\n metadata: { name: REDIS, namespace },\n spec: { selector: { app: REDIS }, ports: [{ port: 6379, targetPort: 6379 }] },\n },\n];\n\n/** These are the objects of the service: a Service of type ClusterIP, and the Deployment with the settings of the cell. */\nexport const serverObjects = (cell: KindCell, namespace: string, image: string, version = 1): unknown[] => {\n const preStop = whole(cell.pod.preStop, \"the preStop sleep\");\n return [\n {\n apiVersion: \"v1\",\n kind: \"Service\",\n metadata: { name: SERVER, namespace },\n spec: { selector: { app: SERVER }, ports: [{ name: \"http\", port: 80, targetPort: PORT }] },\n },\n {\n apiVersion: \"apps/v1\",\n kind: \"Deployment\",\n metadata: { name: SERVER, namespace, labels: { app: SERVER } },\n spec: {\n replicas: cell.pod.replicas,\n selector: { matchLabels: { app: SERVER } },\n strategy: { type: \"RollingUpdate\", rollingUpdate: { maxSurge: \"25%\", maxUnavailable: \"25%\" } },\n minReadySeconds: 0,\n template: {\n metadata: { labels: { app: SERVER, [LOG_LABEL]: \"1\", \"lab.systems/version\": String(version) } },\n spec: {\n terminationGracePeriodSeconds: whole(cell.pod.grace, \"the grace period\"),\n enableServiceLinks: true,\n topologySpreadConstraints: [\n {\n maxSkew: 1,\n topologyKey: \"kubernetes.io/hostname\",\n whenUnsatisfiable: \"ScheduleAnyway\",\n labelSelector: { matchLabels: { app: SERVER } },\n },\n ],\n containers: [\n {\n name: \"app\",\n image,\n imagePullPolicy: \"Never\",\n command: [\"node\", \"app.mjs\"],\n env: [...appEnv(cell), { name: \"LAB_VERSION\", value: String(version) }],\n ports: [{ containerPort: PORT }, { containerPort: HEALTH_PORT }],\n ...(cell.pod.startup === null ? {} : { startupProbe: probeOf(\"/startup\", cell.pod.startup) }),\n readinessProbe: probeOf(\"/readiness\", cell.pod.readiness),\n livenessProbe: probeOf(\"/liveness\", cell.pod.liveness),\n ...(preStop === 0 ? {} : { lifecycle: { preStop: { sleep: { seconds: preStop } } } }),\n },\n ],\n },\n },\n },\n },\n ];\n};\n\n/** These are the objects of the clients: a DaemonSet with one client on each node, the control plane too. */\nexport const clientObjects = (cell: KindCell, namespace: string, image: string, target: string): unknown[] => [\n {\n apiVersion: \"apps/v1\",\n kind: \"DaemonSet\",\n metadata: { name: CLIENT, namespace, labels: { app: CLIENT } },\n spec: {\n selector: { matchLabels: { app: CLIENT } },\n template: {\n metadata: { labels: { app: CLIENT, [LOG_LABEL]: \"1\" } },\n spec: {\n terminationGracePeriodSeconds: 0,\n tolerations: [{ key: \"node-role.kubernetes.io/control-plane\", operator: \"Exists\", effect: \"NoSchedule\" }],\n containers: [\n {\n name: CLIENT,\n image,\n imagePullPolicy: \"Never\",\n command: [\"node\", \"client.mjs\"],\n env: [\n { name: \"TARGET\", value: target },\n { name: \"EVERY_MS\", value: String(valueIn(cell.workload.every, \"ms\")) },\n { name: \"TIMEOUT_MS\", value: String(valueIn(cell.workload.timeout, \"ms\")) },\n { name: \"POD_NAME\", valueFrom: { fieldRef: { fieldPath: \"metadata.name\" } } },\n { name: \"NODE_NAME\", valueFrom: { fieldRef: { fieldPath: \"spec.nodeName\" } } },\n ],\n },\n ],\n },\n },\n },\n },\n];\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport type { Poll, Stage, Timing } from \"@mark1russell7/service\";\nimport { failSpan } from \"@mark1russell7/service\";\nimport { bandOf, seconds, secondsOf } from \"./bands.ts\";\nimport type { Band, Declaration, KubeletProbe } from \"./cell.ts\";\n\n/**\n * This module is the arithmetic of the predictions. Each band is a range of times after a\n * reference event of the run. Each term comes from a declaration, from the pod settings, or from\n * the calibration of the propagation. No term comes from a default. The arithmetic operates in\n * seconds, and each result is a quantity.\n */\n\nconst s = (amount: Quantity): number => valueIn(amount, \"s\");\n/** This function rounds a time to the microsecond, thus a band has no float residue. */\nconst us = (value: number): number => Math.round(value * 1e6) / 1e6;\nconst band = (lo: number, hi: number): Band => bandOf(us(lo), us(hi));\n\n/**\n * A propagation is the time from a change of an endpoint to the rules of each node. A\n * calibration measures it for each kube-proxy mode and node count. Without a calibration, its upper\n * bound is `minSyncPeriod`: an idle kube-proxy syncs a change at once, or one `minSyncPeriod`\n * after its last sync.\n */\nexport interface Propagation extends Band {\n /** This text tells where the bound comes from: a calibration, or the documented `minSyncPeriod`. */\n readonly from: string;\n}\n\n/** This propagation is the documented bound, before a calibration: from zero to one `minSyncPeriod` of 1 s. */\nexport const DOCUMENTED: Propagation = { lo: seconds(0), hi: seconds(1), from: \"minSyncPeriod (1 s), not calibrated\" };\n\n/**\n * This function gives the time from a flip of the probed state to the decision of the kubelet. The\n * next probe comes within one period, then `threshold − 1` more periods follow. A probe of an\n * endpoint that does not answer fails at its timeout.\n */\nexport const kubeletBand = (probe: KubeletProbe, hangs: boolean): Band => {\n const late = hangs ? s(probe.timeout) : 0;\n return band((probe.threshold - 1) * s(probe.period) + late, probe.threshold * s(probe.period) + late);\n};\n\n/**\n * This function gives the time from a fault of a dependency to the trip of a readiness or liveness\n * latch. A dependency that refuses fails each poll at once. A silent dependency fails each poll at\n * the request timeout of its client, or at the timeout of the poll when that is shorter. The\n * watchdog of the client can close the connection sooner, and then each poll fails at once.\n */\nexport const latchBand = (\n timing: Timing,\n poll: Poll,\n failure: \"refuses\" | \"silent\",\n requestTimeout?: Quantity,\n): Band => {\n const error = failSpan(timing, poll, \"error\");\n if (failure === \"refuses\") return band(s(error.min), s(error.max));\n const wait = requestTimeout === undefined ? s(poll.timeout) : Math.min(s(requestTimeout), s(poll.timeout));\n const late = failSpan(timing, { ...poll, timeout: seconds(wait) }, \"timeout\");\n return band(s(error.min), s(late.max));\n};\n\n/** This function gives the time from a fault to the removal of the endpoints from the rules: the latch, then the kubelet, then the propagation. */\nexport const removalBand = (latch: Band, probe: KubeletProbe, propagation: Propagation): Band => {\n const kubelet = secondsOf(kubeletBand(probe, false));\n const trip = secondsOf(latch);\n const spread = secondsOf(propagation);\n return band(trip.lo + kubelet.lo + spread.lo, trip.hi + kubelet.hi + spread.hi);\n};\n\n/**\n * This function gives the time from a fault to the SIGTERM of a replica whose liveness fails with\n * its readiness. The time is the latch, then the liveness probes of the kubelet, then the preStop sleep.\n */\nexport const restartBand = (latch: Band, liveness: KubeletProbe, preStop: Quantity): Band => {\n const kubelet = secondsOf(kubeletBand(liveness, false));\n const trip = secondsOf(latch);\n return band(trip.lo + kubelet.lo + s(preStop), trip.hi + kubelet.hi + s(preStop));\n};\n\n/**\n * This function gives the time from the start of a stall of the event loop to the removal of the\n * replica from the rules. Each probe in the stall fails at its timeout, because the loop does not answer it.\n */\nexport const stallRemovalBand = (readiness: KubeletProbe, propagation: Propagation): Band => {\n const kubelet = secondsOf(kubeletBand(readiness, true));\n const spread = secondsOf(propagation);\n return band(kubelet.lo + spread.lo, kubelet.hi + spread.hi);\n};\n\n/** This function gives the cap of a stage in a declaration, or zero when the declaration does not name the stage. */\nexport const capOf = (declaration: Declaration, stage: Stage): Quantity => {\n const found = declaration.phases.find(([name]) => name === stage);\n return seconds(found === undefined ? 0 : s(found[1]));\n};\n\n/**\n * This function gives the shortest time from the deletion of a pod to the stop of its intake. It\n * is the preStop sleep, then the hold of `fail readiness`. The kubelet adds its own delay before\n * the preStop, thus the real time is longer.\n */\nexport const intakeAfter = (declaration: Declaration, preStop: Quantity): Quantity =>\n seconds(us(s(preStop) + s(capOf(declaration, \"fail readiness\"))));\n\n/**\n * A deletion window is what a deletion predicts on one node. The listener of the pod closes at the\n * stop of its intake. The rules of the node stop the new connections to the pod at the\n * propagation. A connection between the two is refused. Thus the window is empty when the intake\n * stops after the latest propagation.\n */\nexport interface DeletionWindow {\n /** This flag tells that no connection can meet a closed listener. */\n readonly empty: boolean;\n /** This band is the end of the window after the deletion: the propagation. */\n readonly end: Band;\n /** This time is the earliest stop of the intake after the deletion. */\n readonly intake: Quantity;\n}\n\n/** This function gives the deletion window of a declaration and a preStop sleep on a node with a propagation. */\nexport const deletionWindow = (declaration: Declaration, preStop: Quantity, propagation: Propagation): DeletionWindow => {\n const intake = intakeAfter(declaration, preStop);\n const spread = secondsOf(propagation);\n return { empty: s(intake) >= spread.hi, end: band(spread.lo, spread.hi), intake };\n};\n\n/**\n * This function gives the window on a node whose kube-proxy pauses across a deletion. The pause\n * starts `lead` before the deletion and holds for `hold`. The node then syncs in one propagation\n * after the end of the pause, at `resume` after the deletion. The window is empty when the intake\n * stops after that.\n */\nexport const pausedWindow = (\n declaration: Declaration,\n preStop: Quantity,\n lead: Quantity,\n hold: Quantity,\n propagation: Propagation,\n): DeletionWindow & { readonly resume: Quantity } => {\n const intake = intakeAfter(declaration, preStop);\n const resume = us(s(hold) - s(lead));\n const spread = secondsOf(propagation);\n return { empty: s(intake) >= resume + spread.hi, end: band(spread.lo, spread.hi), intake, resume: seconds(resume) };\n};\n\nexport { showBand } from \"./bands.ts\";\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport type { Finding, Rule, Scenario, ServiceDescription, Verdict } from \"@mark1russell7/service\";\nimport { lint } from \"@mark1russell7/service\";\nimport { redisClient } from \"@mark1russell7/service/redis\";\nimport type { KindCell } from \"./cell.ts\";\nimport { compose } from \"./compose.ts\";\nimport type { Propagation } from \"./predict.ts\";\n\n/**\n * These are the rules of the ordering law that a drain in Kubernetes meets, as data. The caller of\n * `lint()` gives the rules, thus they are here and not in the service.\n */\n\n/**\n * O04 with the hold of the service. The listener of a pod stays open through the preStop sleep and\n * then through the hold of `fail readiness`. Thus the sum of the two must cover the propagation of\n * the endpoints. The rule of the research is the case without a hold.\n */\nexport const O04: Rule = {\n id: \"O04\",\n rule: \"the preStop sleep plus the hold of fail readiness is at least the endpoint propagation\",\n window: \"new connections reach a pod whose listener is closed\",\n expr: {\n lhs: {\n terms: [\n { knob: \"prestop_sleep\", coef: 1, at: \"pod\" },\n { knob: \"readiness_hold\", coef: 1, at: \"process\" },\n ],\n },\n op: \">=\",\n rhs: { terms: [{ knob: \"propagation_bound\", coef: 1, at: \"cluster\" }] },\n },\n};\n\n/** O09 is W8: readiness detection plus propagation is no longer than liveness detection plus the preStop sleep. */\nexport const O09: Rule = {\n id: \"O09\",\n rule: \"readiness detection plus endpoint propagation is no longer than liveness detection plus the preStop sleep\",\n window: \"traffic reaches a container that its liveness probe kills\",\n expr: {\n lhs: {\n terms: [\n { knob: \"readiness_detection_window\", coef: 1, at: \"pod\" },\n { knob: \"propagation_bound\", coef: 1, at: \"cluster\" },\n ],\n },\n op: \"<=\",\n rhs: {\n terms: [\n { knob: \"liveness_detection_window\", coef: 1, at: \"pod\" },\n { knob: \"prestop_sleep\", coef: 1, at: \"pod\" },\n ],\n },\n },\n};\n\n/** O10: the preStop sleep plus the drain of the process fits in the grace period. */\nexport const O10: Rule = {\n id: \"O10\",\n rule: \"the preStop sleep plus the shutdown of the process fits in the grace period\",\n window: \"work in flight at SIGKILL is cut, and each connection closes at once\",\n expr: {\n lhs: {\n terms: [\n { knob: \"prestop_sleep\", coef: 1, at: \"pod\" },\n { knob: \"process_drain_time\", coef: 1, at: \"process\" },\n ],\n },\n op: \"<=\",\n rhs: { terms: [{ knob: \"termination_grace\", coef: 1, at: \"pod\" }] },\n },\n};\n\n/** O16: the cooperative window is shorter than the drain, which is shorter than the grace less the preStop sleep. */\nexport const O16: Rule = {\n id: \"O16\",\n rule: \"the cooperative window is shorter than the drain, which is shorter than the grace less the preStop sleep\",\n window: \"handlers are cut before they answer, or the drain itself gets SIGKILL\",\n expr: {\n lhs: { terms: [{ knob: \"cooperative_window\", coef: 1, at: \"process\" }] },\n op: \"<\",\n rhs: { terms: [{ knob: \"process_drain_time\", coef: 1, at: \"process\" }] },\n },\n expr_more: [\n {\n lhs: { terms: [{ knob: \"process_drain_time\", coef: 1, at: \"process\" }] },\n op: \"<\",\n rhs: {\n terms: [\n { knob: \"termination_grace\", coef: 1, at: \"pod\" },\n { knob: \"prestop_sleep\", coef: -1, at: \"pod\" },\n ],\n },\n },\n ],\n};\n\n/** These are the rules of the drain that each kind cell gets. */\nexport const DRAIN_RULES: readonly Rule[] = [O04, O09, O10, O16];\n\nconst seconds = (amount: Quantity): number => valueIn(amount, \"s\");\nconst s = (value: number): Quantity => ({ value, unit: \"s\" });\n\n/**\n * This function gives the scenario of a cell for `lint()`: the preStop sleep, the grace, the probe\n * period and the propagation. The probe windows are the period times the threshold of each probe.\n */\nexport const scenarioOf = (cell: KindCell, propagation: Propagation): Scenario => ({\n platform: {\n \"prestop_sleep@pod\": cell.pod.preStop,\n \"termination_grace@pod\": cell.pod.grace,\n \"probe_period@pod\": cell.pod.readiness.period,\n \"propagation_bound@cluster\": propagation.hi,\n },\n probes: {\n readiness: s(seconds(cell.pod.readiness.period) * cell.pod.readiness.threshold),\n liveness: s(seconds(cell.pod.liveness.period) * cell.pod.liveness.threshold),\n },\n});\n\n/** A verdict set is the verdict of each drain rule for one declaration, by the identifier of the rule. */\nexport type Verdicts = Readonly<Record<string, Verdict>>;\n\n/** This function applies the drain rules to the description of a cell, and gives each finding. */\nexport const lintCell = (description: ServiceDescription, cell: KindCell, propagation: Propagation): Finding[] =>\n lint(description, scenarioOf(cell, propagation), DRAIN_RULES);\n\n/** This function gives the verdict of each drain rule, by its identifier. */\nexport const verdictsOf = (findings: readonly Finding[]): Verdicts =>\n Object.fromEntries(findings.map((finding) => [finding.rule, finding.verdict]));\n\n/** This function gives the description of the service of a cell, as the app of the image composes it. Nothing connects. */\nexport const describeCell = (cell: KindCell): ServiceDescription => {\n const redis = cell.declaration.redis;\n const client =\n redis === null\n ? undefined\n : redisClient(\"redis\", { host: \"redis\", port: 6379, system: \"systems.component.redis\", knobs: redis.knobs, heartbeatMisses: redis.misses });\n return compose(\"lab\", cell.declaration, { redis: client }).describe();\n};\n","import { mkdirSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { StartedTestContainer } from \"testcontainers\";\nimport type { Quantity } from \"@mark1russell7/quantity\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport type { KindCell, KindRun, Replica } from \"./cell.ts\";\nimport type { Cluster } from \"./cluster.ts\";\nimport { command, createCluster } from \"./cluster.ts\";\nimport type { Step } from \"./faults.ts\";\nimport { NODE, POD, VERSION, deletes as deletesAPod, planOf } from \"./faults.ts\";\nimport { labImage } from \"./image.ts\";\nimport type { KindLab, Line, Stamp } from \"./lab.ts\";\nimport { localPin, openKindLab } from \"./lab.ts\";\nimport { CLIENT, REDIS, REDIS_IMAGE, SERVER, clientObjects, namespaceOf, redisObjects, serverObjects } from \"./manifests.ts\";\nimport type { PodLine, Request } from \"./observe.ts\";\nimport type { EnvironmentManifest, KindCellRecord, KindCellResult, LoadSample } from \"./record.ts\";\nimport { FORMAT } from \"./record.ts\";\nimport { scenario } from \"./scenario.ts\";\nimport { judgeKind } from \"./verdict.ts\";\n\n/** This time is the interval of the poll of the rules on each node. */\nexport const RULES_EVERY: Quantity = { value: 50, unit: \"ms\" };\n\n/** These options run a catalog of kind cells. */\nexport interface RunOptions {\n /** This folder gets the raw record of each cell and `summary.ndjson`. */\n readonly out: string;\n readonly log: (line: string) => void;\n /** This count is the number of environments, each a new cluster. */\n readonly environments?: number;\n /** This share of the CPUs of the VM must be idle before a run starts. */\n readonly idle?: number;\n /**\n * This folder holds the run folder of each cluster: its config, its kubeconfig and the cache of\n * kubectl. It is not the output, because a kubeconfig holds credentials.\n */\n readonly work?: string;\n}\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\nconst slug = (name: string): string => name.toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/^-|-$/g, \"\");\n\n/** This tracker keeps the latest state of the cell from the lines: each pod, each slice, the backends of each node and each client. */\nclass Tracker {\n readonly pods = new Map<string, PodLine>();\n readonly slices = new Map<string, Line>();\n readonly backends = new Map<string, readonly string[]>();\n readonly clients = new Map<string, { node: string; ok: number; failed: number }>();\n last = 0;\n see = (line: Line): void => {\n if (typeof line.t === \"number\" && line.t > this.last && line.ev === \"req\") this.last = line.t;\n if (line.ev === \"pod\") {\n const pod = line as unknown as PodLine;\n if (pod.type === \"DELETED\") this.pods.delete(pod.name);\n else this.pods.set(pod.name, pod);\n } else if (line.ev === \"slice\") {\n if (line[\"type\"] === \"DELETED\") this.slices.delete(String(line[\"name\"]));\n else this.slices.set(String(line[\"name\"]), line);\n } else if (line.ev === \"rules\") {\n this.backends.set(String(line[\"node\"]), line[\"b\"] as readonly string[]);\n } else if (line.ev === \"req\") {\n const request = line as unknown as Request;\n const client = this.clients.get(request.p) ?? { node: request.n, ok: 0, failed: 0 };\n if (request.o === \"ok\") client.ok = Math.max(client.ok, request.t);\n else client.failed = Math.max(client.failed, request.t);\n this.clients.set(request.p, client);\n }\n };\n servers(): PodLine[] {\n return [...this.pods.values()].filter((pod) => pod.labels[\"app\"] === SERVER).sort((a, b) => a.name.localeCompare(b.name));\n }\n}\n\n/** A context is the state of one cell in one environment. */\ninterface Context {\n readonly lab: KindLab;\n readonly cluster: Cluster;\n readonly cell: KindCell;\n readonly namespace: string;\n readonly tracker: Tracker;\n readonly clusterIp: string;\n readonly loads: LoadSample[];\n readonly loader: StartedTestContainer;\n readonly idle: number;\n readonly log: (line: string) => void;\n version: number;\n probed: string;\n /** This count is the replicas of the service at rest: one less while a scale-in holds. */\n expected: number;\n}\n\n/** This function tells why the cell is not at rest, or gives null when it is. */\nconst unsteady = (ctx: Context): string | null => {\n const { tracker, cell } = ctx;\n const servers = tracker.servers();\n if (servers.length !== ctx.expected) return `${servers.length} replicas, not ${ctx.expected}`;\n const busy = servers.find((pod) => pod.del || !pod.ready);\n if (busy !== undefined) return `${busy.name} is not ready`;\n if (new Set(servers.map((pod) => pod.labels[\"pod-template-hash\"])).size > 1) return \"two versions of the replicas\";\n if (cell.declaration.redis !== null) {\n const redis = [...tracker.pods.values()].filter((pod) => pod.labels[\"app\"] === REDIS);\n if (redis.length !== 1 || !redis[0]!.ready || redis[0]!.del) return \"Redis is not ready\";\n }\n const ips = servers.map((pod) => pod.ip ?? \"\").sort();\n const ready = [...tracker.slices.values()]\n .filter((slice) => slice[\"svc\"] === SERVER)\n .flatMap((slice) => (slice[\"eps\"] as Array<{ ip: string; r: boolean }>).filter((endpoint) => endpoint.r).map((endpoint) => endpoint.ip))\n .sort();\n if (ready.join() !== ips.join()) return `the EndpointSlice has ${ready.join(\" \")}, not ${ips.join(\" \")}`;\n const expected = ips.map((ip) => `${ip}:8080`).sort().join();\n for (const node of ctx.cluster.nodes)\n if ((tracker.backends.get(node) ?? []).join() !== expected) return `the rules of ${node} have ${(tracker.backends.get(node) ?? []).join(\" \")}`;\n const nodes = new Set([...tracker.clients.values()].map((client) => client.node));\n if (nodes.size !== ctx.cluster.nodes.length) return `clients answer on ${nodes.size} nodes`;\n for (const [pod, client] of tracker.clients) {\n if (tracker.last - client.ok > 1000) return `client ${pod} has no answer in the last second`;\n if (tracker.last - client.failed < 2000) return `client ${pod} failed in the last 2 s`;\n }\n return null;\n};\n\n/** This function waits until the cell is at rest, and gives null, or the reason when it is not at rest in time. */\nconst steady = async (ctx: Context, within: number): Promise<string | null> => {\n const end = Date.now() + within;\n let why = unsteady(ctx);\n while (why !== null && Date.now() < end) {\n await sleep(200);\n why = unsteady(ctx);\n }\n return why;\n};\n\n/** This function samples the load of the VM for 4 s, and names the lab containers that are not of this run. */\nconst sample = async (ctx: Context, ours: ReadonlySet<string>): Promise<LoadSample> => {\n const result = await ctx.loader.exec([\"top\", \"-b\", \"-n\", \"2\", \"-d\", \"4\"]);\n const second = result.stdout.split(/^Mem:/m).at(-1) ?? \"\";\n const idle = Number(/(\\d+)% idle/.exec(second)?.[1] ?? \"0\") / 100;\n const top = second\n .split(\"\\n\")\n .filter((line) => /^\\s*\\d+\\s/.test(line))\n .slice(0, 6)\n .map((line) => line.trim().replace(/\\s+/g, \" \"));\n const listed = (await command(\"docker\", [\"ps\", \"--format\", \"{{.ID}} {{.Names}} {{.Image}}\"])).trim().split(\"\\n\");\n const foreign = listed\n .filter((line) => line !== \"\")\n .filter((line) => !ours.has(line.split(\" \")[1]!) && !ours.has(line.split(\" \")[0]!))\n .filter((line) => /loom-|kindest\\/node|toxiproxy|haproxy|redis|systems-kind-lab|f6-/.test(line.split(\" \")[2] ?? \"\"));\n return { t: Date.now(), idle, top, foreign };\n};\n\n/** This function waits until the VM is quiet: no lab container of another agent, and an idle share of the CPUs. */\nconst quiet = async (ctx: Context, ours: ReadonlySet<string>): Promise<LoadSample> => {\n for (;;) {\n // the prober and the watcher start again with new names, thus the names of this lab come at each sample\n const load = await sample(ctx, new Set([...ours, ...ctx.lab.own(), ctx.loader.getName().replace(/^\\//, \"\")]));\n ctx.loads.push(load);\n if (load.foreign.length === 0 && load.idle >= ctx.idle) return load;\n ctx.log(` waiting: ${Math.round(load.idle * 100)}% idle, other lab containers: ${load.foreign.join(\"; \") || \"none\"}`);\n await sleep(30_000);\n }\n};\n\n/** This function deploys a cell in its namespace, with the watcher, the poll of the rules and the clients. */\nconst deploy = async (lab: KindLab, cluster: Cluster, cell: KindCell, namespace: string, redisImage: string): Promise<string> => {\n await cluster.apply([{ apiVersion: \"v1\", kind: \"Namespace\", metadata: { name: namespace } }]);\n await lab.watch([namespace]);\n if (cell.declaration.redis !== null) {\n await cluster.apply(redisObjects(namespace, redisImage));\n await cluster.kubectl([\"-n\", namespace, \"rollout\", \"status\", `deployment/${REDIS}`, \"--timeout=180s\"]);\n }\n await cluster.apply(serverObjects(cell, namespace, lab.image));\n const clusterIp = (await cluster.kubectl([\"-n\", namespace, \"get\", \"service\", SERVER, \"-o\", \"jsonpath={.spec.clusterIP}\"])).trim();\n await cluster.apply(clientObjects(cell, namespace, lab.image, `${clusterIp}:80`));\n await cluster.kubectl([\"-n\", namespace, \"rollout\", \"status\", `deployment/${SERVER}`, \"--timeout=300s\"]);\n await cluster.kubectl([\"-n\", namespace, \"rollout\", \"status\", `daemonset/${CLIENT}`, \"--timeout=300s\"]);\n await lab.rules({ mode: cluster.scenario.kubeProxy.mode, ns: namespace, svc: SERVER, ip: clusterIp, port: 80, every: RULES_EVERY });\n return clusterIp;\n};\n\n/** This function starts the prober again when the pods of the cell changed: it watches the namespace of each pod. */\nconst probeThePods = async (ctx: Context): Promise<void> => {\n const pods = [...ctx.tracker.pods.values()].filter((pod) => !pod.del && pod.node !== null && pod.cs.every((status) => status.st === \"running\"));\n const key = pods.map((pod) => `${pod.name}/${pod.cs.map((status) => status.id).join(\"+\")}`).sort().join();\n if (key === ctx.probed) return;\n const netns: number[] = [];\n for (const pod of pods) {\n const container = pod.labels[\"app\"] === SERVER ? \"app\" : pod.labels[\"app\"] === CLIENT ? CLIENT : REDIS;\n const pid = await ctx.lab.pidOf(pod.node!, ctx.namespace, pod.name, container);\n netns.push(await ctx.lab.netnsOf(pod.node!, pid));\n }\n await ctx.lab.probe([...new Set(netns)]);\n ctx.probed = key;\n};\n\n/** This function gives the replicas of the service at this time, in the sequence of their names. */\nconst replicasOf = (ctx: Context): Replica[] =>\n ctx.tracker.servers().map((pod) => ({ pod: pod.name, node: pod.node ?? \"\", ip: pod.ip ?? \"\", version: pod.labels[\"pod-template-hash\"] ?? \"\" }));\n\n/** This function does one step of a fault plan. It gives the stamp of its act, or null for an act without a stamp. */\nconst doStep = async (ctx: Context, step: Step, replicas: readonly Replica[], background: Array<Promise<unknown>>): Promise<Stamp | null> => {\n const { lab, cluster, namespace } = ctx;\n const target = replicas[0]!;\n const fill = (args: readonly string[]): string[] =>\n args.map((arg) =>\n arg.replaceAll(\"{namespace}\", namespace).replaceAll(POD, target.pod).replaceAll(NODE, target.node).replaceAll(VERSION, String(ctx.version)),\n );\n const where = async (which: \"server\" | \"redis\" | \"kube-proxy\"): Promise<{ node: string; pid: number; pod: PodLine | null }> => {\n if (which === \"server\") return { node: target.node, pid: await lab.pidOf(target.node, namespace, target.pod, \"app\"), pod: null };\n if (which === \"redis\") {\n const redis = [...ctx.tracker.pods.values()].find((pod) => pod.labels[\"app\"] === REDIS && !pod.del)!;\n return { node: redis.node!, pid: await lab.pidOf(redis.node!, namespace, redis.name, REDIS), pod: redis };\n }\n const worker = cluster.nodes[1] ?? cluster.nodes[0]!;\n const proxy = JSON.parse(\n await cluster.kubectl([\"-n\", \"kube-system\", \"get\", \"pods\", \"-l\", \"k8s-app=kube-proxy\", \"--field-selector\", `spec.nodeName=${worker}`, \"-o\", \"json\"]),\n ) as { items: Array<{ metadata: { name: string } }> };\n return { node: worker, pid: await lab.pidOf(worker, \"kube-system\", proxy.items[0]!.metadata.name, \"kube-proxy\"), pod: null };\n };\n switch (step.do) {\n case \"kubectl\": {\n const t0 = await lab.now();\n if (step.args.includes(`LAB_VERSION=${VERSION}`)) ctx.version += 1;\n const call = cluster.kubectl(fill(step.args));\n if (step.background === true) {\n background.push(call.catch((error: unknown) => ctx.log(` kubectl ${step.args.join(\" \")}: ${String(error)}`)));\n return { t0, t1: t0 };\n }\n await call;\n return { t0, t1: await lab.now() };\n }\n case \"signal\": {\n const at = await where(step.target);\n return lab.act(at.node, { op: \"signal\", pid: at.pid, signal: step.signal });\n }\n case \"pause\": {\n const at = await where(step.target);\n const stamp = lab.act(at.node, { op: \"pause\", pid: at.pid, ms: valueIn(step.time, \"ms\") });\n background.push(stamp);\n const now = await lab.now();\n return { t0: now, t1: now };\n }\n case \"inject\": {\n const at = await where(step.target);\n let first: Stamp | null = null;\n for (const replica of replicas) {\n const stamp = await lab.inject(at.node, at.pid, [step.action, replica.ip]);\n first ??= stamp;\n }\n return first;\n }\n case \"flag\": {\n const at = await where(step.target);\n return step.on\n ? lab.act(at.node, { op: \"write\", path: `/proc/${at.pid}/root/tmp/lab-fail`, data: \"1\" })\n : lab.act(at.node, { op: \"remove\", path: `/proc/${at.pid}/root/tmp/lab-fail` });\n }\n case \"cpu\": {\n const at = await where(step.target);\n const cgroup = await lab.cgroupOf(at.node, at.pid);\n return lab.act(at.node, { op: \"write\", path: `${cgroup}/cpu.max`, data: step.max });\n }\n case \"stall\": {\n const at = await where(step.target);\n return lab.act(at.node, { op: \"stall\", pid: at.pid, ms: valueIn(step.time, \"ms\") });\n }\n case \"wait\":\n await sleep(valueIn(step.time, \"ms\"));\n return null;\n }\n};\n\n/** This function tells if a run is over: the trigger is done, and the cell is at rest again. */\nconst over = (\n ctx: Context,\n run: { readonly target: Replica | null; readonly replicas: readonly Replica[]; readonly restarts: ReadonlyMap<string, number> },\n lines: readonly Line[],\n): boolean => {\n if (unsteady(ctx) !== null) return false;\n const kind = ctx.cell.trigger.kind;\n if ((kind === \"crash\" || kind === \"liveness-restart\") && run.target !== null) {\n const pod = ctx.tracker.pods.get(run.target.pod);\n const now = pod === undefined ? 0 : pod.cs.reduce((sum, status) => sum + status.rc, 0);\n return now > (run.restarts.get(run.target.pod) ?? 0);\n }\n if (run.target !== null && [\"pod-deletion\", \"node-drain\", \"scale-in\", \"freeze-process\"].includes(kind) && ctx.cell.template === \"T07\")\n return kind === \"scale-in\" || kind === \"node-drain\"\n ? run.replicas.some((replica) => !ctx.tracker.pods.has(replica.pod))\n : !ctx.tracker.pods.has(run.target.pod);\n if (kind === \"rolling-update\" || kind === \"rollout\") return run.replicas.every((replica) => !ctx.tracker.pods.has(replica.pod));\n if (kind === \"app-stall\") return lines.some((line) => line.ev === \"stall\" && line[\"phase\"] === \"end\");\n return true;\n};\n\n/** This function does one run of a cell: at rest, the trigger, the heal, then the observation until the cell is at rest again. */\nconst runOnce = async (ctx: Context, env: number, index: number, warmup: boolean, ours: Set<string>): Promise<KindRun> => {\n const { lab, cell, cluster, namespace } = ctx;\n const plan = planOf(cell);\n const atRest = await steady(ctx, 300_000);\n const empty = (why: string, start: number): KindRun => ({\n env, index, warmup, start, end: start, acts: { fault: null, heal: null, resume: null }, target: null, paused: null, replicas: [], void: why, lines: lab.take(), dropped: {},\n });\n if (atRest !== null) return empty(`the cell was not at rest: ${atRest}`, await lab.now());\n await probeThePods(ctx);\n await quiet(ctx, ours);\n if ((await steady(ctx, 300_000)) !== null) return empty(\"the cell was not at rest after the wait for a quiet VM\", await lab.now());\n const replicas = replicasOf(ctx);\n const restarts = new Map(ctx.tracker.servers().map((pod) => [pod.name, pod.cs.reduce((sum, status) => sum + status.rc, 0)]));\n const target = cell.declaration.redis !== null && [\"rst\", \"blackhole\", \"freeze-process\", \"peer-gone-without-close\", \"rollout\"].includes(cell.trigger.kind) ? null : replicas[0]!;\n const paused = cell.template === \"T07\" && cell.trigger.kind === \"freeze-process\" ? (cluster.nodes[1] ?? cluster.nodes[0]!) : null;\n lab.take();\n for (const key of Object.keys(lab.dropped)) delete lab.dropped[key];\n const start = await lab.now();\n const background: Array<Promise<unknown>> = [];\n let fault: number | null = null;\n let resume: number | null = null;\n if (cell.trigger.kind === \"scale-in\") ctx.expected = cell.pod.replicas - 1;\n const notes: string[] = [];\n for (const step of plan.inject) {\n const stamp = await doStep(ctx, step, replicas, background);\n if (stamp !== null && fault === null) fault = stamp.t0;\n }\n let heal: number | null = null;\n if (plan.hold !== null) {\n const now = await lab.now();\n await sleep(Math.max(0, valueIn(plan.hold, \"ms\") - (now - (fault ?? now))));\n for (const step of plan.heal) {\n try {\n const stamp = await doStep(ctx, step, replicas, background);\n if (stamp !== null && heal === null) heal = stamp.t0;\n } catch (error) {\n notes.push(`the heal ${step.do} failed: ${String(error)}`);\n }\n }\n }\n const settled = await Promise.allSettled(background);\n for (const each of settled) {\n if (each.status === \"fulfilled\" && typeof each.value === \"object\" && each.value !== null && \"t1\" in each.value) resume = (each.value as Stamp).t1;\n }\n // the observation: at least 3 s after the last act, until the trigger happened and the cell is at rest again\n const limit = Date.now() + 240_000 + valueIn(cell.pod.grace, \"ms\") + valueIn(cell.pod.preStop, \"ms\");\n await sleep(3000);\n while (Date.now() < limit && !over(ctx, { target, replicas, restarts }, lab.lines)) await sleep(250);\n const end = await lab.now();\n const timedOut = Date.now() >= limit;\n for (const step of plan.restore) {\n try {\n await doStep(ctx, step, replicas, background);\n } catch (error) {\n notes.push(`the restore ${step.do} failed: ${String(error)}`);\n }\n }\n ctx.expected = cell.pod.replicas;\n for (const note of notes) ctx.log(` ${note}`);\n const lines = lab.take();\n // the replica that the trigger stopped: for a scale-in or a drain, the controller chooses it\n const stopped = deletesAPod(cell) ? (replicas.find((replica) => lines.some((line) => line.ev === \"pod\" && line[\"name\"] === replica.pod && line[\"del\"] === true)) ?? target) : target;\n const restarted = lines.some((line) => line.ev === \"pod\" && (line[\"cs\"] as Array<{ rc: number }>).some((status) => status.rc > 0));\n if (restarted) {\n // a container that restarted gets a back-off on its next restart: new pods start without one\n await cluster.kubectl([\"-n\", namespace, \"rollout\", \"restart\", `deployment/${SERVER}`]);\n await cluster.kubectl([\"-n\", namespace, \"rollout\", \"status\", `deployment/${SERVER}`, \"--timeout=300s\"]);\n }\n if (cell.trigger.kind === \"node-drain\") await rebalance(ctx);\n ctx.log(` ${warmup ? \"warm-up\" : `run ${index}`}: ${sec(end - start)} s${timedOut ? \", not at rest in time\" : \"\"}`);\n return {\n env, index, warmup, start, end, acts: { fault, heal, resume }, target: stopped, paused, replicas, void: null, lines, dropped: { ...lab.dropped }, notes,\n };\n};\n\nconst sec = (ms: number): number => Math.round(ms) / 1000;\n\n/** This function moves a replica back to a node without one after a drain: it deletes one replica of the node with two. */\nconst rebalance = async (ctx: Context): Promise<void> => {\n await steady(ctx, 300_000);\n const servers = ctx.tracker.servers();\n const byNode = new Map<string, string[]>();\n for (const pod of servers) byNode.set(pod.node ?? \"\", [...(byNode.get(pod.node ?? \"\") ?? []), pod.name]);\n const crowded = [...byNode.values()].find((pods) => pods.length > 1);\n if (crowded !== undefined && byNode.size < ctx.cluster.nodes.length - 1)\n await ctx.cluster.kubectl([\"-n\", ctx.namespace, \"delete\", \"pod\", crowded[0]!, \"--wait=true\"]);\n};\n\n/** This function writes the result of a cell: its raw record, and one line of its verdict in `summary.ndjson`. */\nexport const writeResult = (out: string, result: KindCellResult): void => {\n mkdirSync(out, { recursive: true });\n const record: KindCellRecord = { format: FORMAT, ...result };\n writeFileSync(join(out, `${slug(result.cell.name)}.json`), JSON.stringify(record));\n const { verdict, cell } = result;\n writeFileSync(\n join(out, \"summary.ndjson\"),\n `${JSON.stringify({\n id: cell.id,\n name: cell.name,\n mode: cell.mode,\n trigger: cell.trigger.kind,\n prediction: cell.prediction.kind,\n decision: verdict.decision,\n counted: verdict.counted,\n voided: verdict.voided,\n failed: verdict.failed,\n median: verdict.median,\n why: verdict.why,\n })}\\n`,\n { flag: \"a\" },\n );\n};\n\n/**\n * This function does the runs of a catalog of kind cells. The cells of one kube-proxy mode and node\n * count share a cluster. Each group gets new clusters in sequence, one for each environment. Each\n * environment does its share of the runs of each cell, after one warm-up. The function deletes each\n * cluster after its cells.\n */\nexport const runKindCells = async (cells: readonly KindCell[], options: RunOptions): Promise<KindCellResult[]> => {\n const environments = options.environments ?? 2;\n const image = await labImage();\n const redisImage = await localPin(REDIS_IMAGE);\n const groups = new Map<string, KindCell[]>();\n for (const cell of cells) groups.set(`${cell.mode}-${cell.workers}`, [...(groups.get(`${cell.mode}-${cell.workers}`) ?? []), cell]);\n const results: KindCellResult[] = [];\n for (const [key, group] of groups) {\n const partial = new Map<string, { runs: KindRun[]; manifests: EnvironmentManifest[]; loads: LoadSample[] }>();\n for (let env = 0; env < environments; env += 1) {\n const s = scenario(`lab-${key}-${env}`, group[0]!.mode, group[0]!.workers);\n const dir = join(options.work ?? join(tmpdir(), \"systems-kind-work\"), s.name);\n options.log(`cluster ${s.name}: ${s.kubeProxy.mode}, ${s.workers} workers`);\n const cluster = await createCluster(s, dir);\n let lab: KindLab | null = null;\n let loader: StartedTestContainer | null = null;\n try {\n await cluster.load(image);\n await cluster.load(redisImage);\n lab = await openKindLab(cluster, image);\n const { GenericContainer } = await import(\"testcontainers\");\n const Shared = class extends GenericContainer {\n withPidMode(mode: string): this {\n (this as unknown as { hostConfig: { PidMode?: string } }).hostConfig.PidMode = mode;\n return this;\n }\n };\n loader = await new Shared(image).withPidMode(\"host\").withNetworkMode(\"none\").withCommand([\"sleep\", \"infinity\"]).start();\n const manifest = await cluster.manifest();\n const ours = new Set<string>([...cluster.nodes]);\n for (const cell of group) {\n const namespace = namespaceOf(cell);\n options.log(`${cell.name} (${cell.id}), environment ${env}`);\n const tracker = new Tracker();\n const off = lab.listen(tracker.see);\n const loads: LoadSample[] = [];\n try {\n const clusterIp = await deploy(lab, cluster, cell, namespace, redisImage);\n for (const name of [...lab.own(), loader.getName().replace(/^\\//, \"\")]) ours.add(name);\n const ctx: Context = { lab, cluster, cell, namespace, tracker, clusterIp, loads, loader, idle: options.idle ?? 0.6, log: options.log, version: 1, probed: \"\", expected: cell.pod.replicas };\n const share = Math.ceil(cell.runs / environments) - (env < cell.runs % environments || cell.runs % environments === 0 ? 0 : 1);\n const runs: KindRun[] = [await runOnce(ctx, env, -1, true, ours)];\n for (let index = 0; index < share; index += 1) runs.push(await runOnce(ctx, env, index, false, ours));\n const entry = partial.get(cell.name) ?? { runs: [], manifests: [], loads: [] };\n entry.runs.push(...runs);\n entry.manifests.push({ env, scenario: s, cluster: manifest, images: { lab: image, redis: redisImage } });\n entry.loads.push(...loads);\n partial.set(cell.name, entry);\n mkdirSync(join(options.out, \"partial\"), { recursive: true });\n writeFileSync(join(options.out, \"partial\", `${slug(cell.name)}.env${env}.json`), JSON.stringify({ cell, runs, manifest, loads }));\n } finally {\n off();\n await lab.rules(null).catch(() => undefined);\n await lab.stopProbe();\n await cluster.kubectl([\"delete\", \"namespace\", namespace, \"--wait=true\", \"--timeout=300s\"]).catch((error: unknown) => options.log(` ${String(error)}`));\n }\n }\n } finally {\n if (loader !== null) await loader.stop({ timeout: 0 }).catch(() => undefined);\n if (lab !== null) await lab.close();\n await cluster.delete();\n options.log(`cluster ${s.name} deleted`);\n }\n }\n for (const cell of group) {\n const entry = partial.get(cell.name);\n if (entry === undefined) continue;\n const result: KindCellResult = { cell, ...entry, verdict: judgeKind(cell, entry.runs), at: new Date().toISOString() };\n writeResult(options.out, result);\n rmSync(join(options.out, \"partial\", `${slug(cell.name)}.env0.json`), { force: true });\n for (let env = 1; env < environments; env += 1) rmSync(join(options.out, \"partial\", `${slug(cell.name)}.env${env}.json`), { force: true });\n options.log(`${cell.name}: ${result.verdict.decision}, ${result.verdict.why}`);\n results.push(result);\n }\n }\n return results;\n};\n\n/** This function gives the estimate of a cell in seconds, for the arithmetic of the shards. */\nconst estimate = (cell: KindCell, environments = 2): number => {\n const hold = cell.trigger.hold === undefined ? 0 : valueIn(cell.trigger.hold, \"s\");\n const trigger: Record<string, number> = {\n \"liveness-restart\": 100,\n crash: 30,\n \"app-stall\": hold + 40,\n \"cpu-throttle\": hold + 40,\n rollout: hold + 50,\n };\n const each = 25 + (trigger[cell.trigger.kind] ?? (cell.template === \"T18\" ? hold + 35 : 15 + valueIn(cell.pod.preStop, \"s\")));\n return 60 * environments + (cell.runs + environments) * each;\n};\n\n/**\n * This function gives an estimate of the time of a cell: its setup, then each run and each\n * warm-up. A run waits for the cell at rest, samples the load, does the trigger and waits until the\n * cell is at rest again.\n */\nexport const estimatedTime = (cell: KindCell, environments = 2): Quantity => ({ value: estimate(cell, environments), unit: \"s\" });\n\n/**\n * This function gives the share of a shard of a catalog: cells in sequence. Thus the cells of one\n * mode stay together and share clusters. Each shard gets an equal share of the estimated time.\n */\nexport const shardOf = <T extends KindCell>(cells: readonly T[], index: number, count: number): T[] => {\n if (!(Number.isInteger(index) && Number.isInteger(count) && count >= 1 && index >= 0 && index < count))\n throw new RangeError(`shard ${index} of ${count} is not a shard`);\n const ordered = [...cells].sort((a, b) => (a.mode === b.mode ? 0 : a.mode < b.mode ? -1 : 1));\n const total = ordered.reduce((sum, cell) => sum + estimate(cell), 0);\n const out: T[] = [];\n let before = 0;\n for (const cell of ordered) {\n const middle = before + estimate(cell) / 2;\n if (Math.min(count - 1, Math.floor((middle / total) * count)) === index) out.push(cell);\n before += estimate(cell);\n }\n return out;\n};\n"],"mappings":"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"}