feat: platform backbone and field capture, first part (checkpoint)
Platform backbone (plan 2026-10-13-plattform-rueckgrat), reviewed tasks: - run roles api/worker/job/all with shared startup checks (Task 1) - schema and migration for DomainEvent, deliveries, JobRun, ThrottlerHit (Task 2) - audit, notifications and domain events written in the same transaction (Task 3) - dispatcher with SKIP LOCKED claims, retries and stale release (Task 4) - worker building blocks: tick, LISTEN/NOTIFY wake-up, health (Task 5) - worker process and entry points (Task 6) - throttler counters in Postgres (Task 19) - separate keys per purpose with key ring (Task 20) - public origin page without exact location (Task 22) - shop keys split into read and write (Task 23) - Kustomize base, components, overlay encry, CI validation (Task 26) Field capture T3 (plan 2026-10-13-t3-erfassung-am-stand), reviewed tasks: - brood frames, read endpoints, apiary overview and offline pack - network basics, offline queue v3, navigation sheets, form building blocks - new inspection with last-time hints and weather, varroa per day, treatment prefill, apiary page with round - inspection photo (Task 10) is implemented, review pending Not rolled out. Remaining tasks follow in later commits. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
10cbe9bf2d
commit
c01a38835a
340 files changed
+25695
-1808
No files matched your search
@@ -0,0 +1,161 @@
|
||||
// Kubernetes semantics for rendered manifests: pod templates, label
|
||||
// selectors and NetworkPolicies (the union of all policies that select a
|
||||
// pod, both the egress and the ingress side). Used by
|
||||
// manifest-policy.test.mjs to ask "can A reach B on port P".
|
||||
|
||||
export const NAMESPACE = 'apicore';
|
||||
|
||||
/** Pod template of a workload, or undefined for anything else. */
|
||||
export function podTemplateOf(doc) {
|
||||
if (doc.kind === 'CronJob') return doc.spec?.jobTemplate?.spec?.template;
|
||||
if (['Deployment', 'StatefulSet', 'DaemonSet', 'Job'].includes(doc.kind)) return doc.spec?.template;
|
||||
return doc.kind === 'Pod' ? doc : undefined;
|
||||
}
|
||||
|
||||
function podsOf(docs) {
|
||||
return docs.flatMap((doc) => {
|
||||
const template = podTemplateOf(doc);
|
||||
if (!template) return [];
|
||||
const namespace = doc.metadata.namespace ?? NAMESPACE;
|
||||
const containers = template.spec.containers ?? [];
|
||||
return [{
|
||||
kind: 'pod',
|
||||
owner: `${doc.kind}/${doc.metadata.name}`,
|
||||
doc,
|
||||
namespace,
|
||||
nsLabels: { 'kubernetes.io/metadata.name': namespace },
|
||||
labels: template.metadata?.labels ?? {},
|
||||
spec: template.spec,
|
||||
containers,
|
||||
initContainers: template.spec.initContainers ?? [],
|
||||
ports: containers.flatMap((c) => c.ports ?? []),
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
/** A pod that is not rendered here (Traefik, CoreDNS, cert-manager solver). */
|
||||
export function syntheticPod(namespace, labels, ports = []) {
|
||||
return {
|
||||
kind: 'pod', owner: `synthetic/${namespace}/${JSON.stringify(labels)}`, namespace,
|
||||
nsLabels: { 'kubernetes.io/metadata.name': namespace }, labels, ports,
|
||||
};
|
||||
}
|
||||
|
||||
export const ip = (address) => ({ kind: 'ip', owner: `ip/${address}`, ip: address });
|
||||
|
||||
export function matchesSelector(selector, labels) {
|
||||
if (!selector) return true;
|
||||
const plain = Object.entries(selector.matchLabels ?? {}).every(([k, v]) => labels[k] === v);
|
||||
const expressions = (selector.matchExpressions ?? []).every(({ key, operator, values = [] }) => {
|
||||
if (operator === 'In') return values.includes(labels[key]);
|
||||
if (operator === 'NotIn') return !values.includes(labels[key]);
|
||||
if (operator === 'Exists') return key in labels;
|
||||
if (operator === 'DoesNotExist') return !(key in labels);
|
||||
throw new Error(`unsupported selector operator ${operator}`);
|
||||
});
|
||||
return plain && expressions;
|
||||
}
|
||||
|
||||
export const isEmptySelector = (s) =>
|
||||
!s || (Object.keys(s.matchLabels ?? {}).length === 0 && (s.matchExpressions ?? []).length === 0);
|
||||
|
||||
export function policyTypes(policy) {
|
||||
return policy.spec.policyTypes ?? (policy.spec.egress ? ['Ingress', 'Egress'] : ['Ingress']);
|
||||
}
|
||||
|
||||
function parseIp(address) {
|
||||
if (address.includes(':')) {
|
||||
const [head, tail] = address.includes('::') ? address.split('::') : [address, null];
|
||||
const h = head ? head.split(':') : [];
|
||||
const t = tail ? tail.split(':') : [];
|
||||
const groups = tail === null ? h : [...h, ...Array(8 - h.length - t.length).fill('0'), ...t];
|
||||
return { bits: 128, value: groups.reduce((acc, g) => (acc << 16n) + BigInt(parseInt(g, 16)), 0n) };
|
||||
}
|
||||
return { bits: 32, value: address.split('.').reduce((acc, o) => (acc << 8n) + BigInt(Number(o)), 0n) };
|
||||
}
|
||||
|
||||
export function cidrContains(cidr, address) {
|
||||
const [network, prefix] = cidr.split('/');
|
||||
const net = parseIp(network);
|
||||
const host = parseIp(address);
|
||||
if (net.bits !== host.bits) return false;
|
||||
const shift = BigInt(net.bits - Number(prefix));
|
||||
return net.value >> shift === host.value >> shift;
|
||||
}
|
||||
|
||||
function peerMatches(peer, endpoint, policyNamespace) {
|
||||
if (peer.ipBlock) {
|
||||
return (
|
||||
endpoint.kind === 'ip' &&
|
||||
cidrContains(peer.ipBlock.cidr, endpoint.ip) &&
|
||||
!(peer.ipBlock.except ?? []).some((cidr) => cidrContains(cidr, endpoint.ip))
|
||||
);
|
||||
}
|
||||
if (endpoint.kind !== 'pod') return false;
|
||||
const namespaceOk = peer.namespaceSelector
|
||||
? matchesSelector(peer.namespaceSelector, endpoint.nsLabels)
|
||||
: endpoint.namespace === policyNamespace;
|
||||
return namespaceOk && matchesSelector(peer.podSelector, endpoint.labels);
|
||||
}
|
||||
|
||||
function portMatches(ports, port, protocol, destination) {
|
||||
if (!ports || ports.length === 0) return true;
|
||||
return ports.some((p) => {
|
||||
if ((p.protocol ?? 'TCP') !== protocol) return false;
|
||||
if (p.port === undefined) return true;
|
||||
if (typeof p.port === 'string') {
|
||||
return (destination.ports ?? []).some(
|
||||
(c) => c.name === p.port && c.containerPort === port && (c.protocol ?? 'TCP') === protocol,
|
||||
);
|
||||
}
|
||||
return p.endPort ? port >= p.port && port <= p.endPort : p.port === port;
|
||||
});
|
||||
}
|
||||
|
||||
/** Everything the tests need to reason about one rendered document set. */
|
||||
export function modelOf(docs) {
|
||||
const policies = docs.filter((d) => d.kind === 'NetworkPolicy');
|
||||
const pods = podsOf(docs);
|
||||
|
||||
const policiesFor = (pod, type) =>
|
||||
policies.filter(
|
||||
(p) =>
|
||||
(p.metadata.namespace ?? NAMESPACE) === pod.namespace &&
|
||||
matchesSelector(p.spec.podSelector, pod.labels) &&
|
||||
policyTypes(p).includes(type),
|
||||
);
|
||||
|
||||
function allows(direction, pod, peer, port, protocol) {
|
||||
const type = direction === 'egress' ? 'Egress' : 'Ingress';
|
||||
const relevant = policiesFor(pod, type);
|
||||
if (relevant.length === 0) return true;
|
||||
const destination = direction === 'egress' ? peer : pod;
|
||||
return relevant.some((policy) =>
|
||||
(policy.spec[direction] ?? []).some((rule) => {
|
||||
const peers = rule[direction === 'egress' ? 'to' : 'from'];
|
||||
const peerOk =
|
||||
!peers || peers.length === 0 ||
|
||||
peers.some((p) => peerMatches(p, peer, policy.metadata.namespace ?? NAMESPACE));
|
||||
return peerOk && portMatches(rule.ports, port, protocol, destination);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const allowsEgress = (src, dst, port, protocol = 'TCP') => allows('egress', src, dst, port, protocol);
|
||||
const allowsIngress = (dst, src, port, protocol = 'TCP') => allows('ingress', dst, src, port, protocol);
|
||||
const canConnect = (src, dst, port, protocol = 'TCP') =>
|
||||
(src.kind !== 'pod' || allowsEgress(src, dst, port, protocol)) &&
|
||||
(dst.kind !== 'pod' || allowsIngress(dst, src, port, protocol));
|
||||
|
||||
return {
|
||||
docs,
|
||||
pods,
|
||||
policies,
|
||||
policiesFor,
|
||||
allowsEgress,
|
||||
allowsIngress,
|
||||
canConnect,
|
||||
byApp: (app) => pods.filter((p) => p.labels.app === app),
|
||||
byOwner: (owner) => pods.find((p) => p.owner === owner),
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user