5a6dedd9835c1817298deaadc25a9a394d3df763
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
22977c183f
|
feat(template-chart)!: ambientMesh becomes an object, and the CI exception gets a pinned expectation
Gitea #143, maintainer's instruction. Both numbers this switch renders are ISTIO CONSTANTS rather than standards, and a value with a documented default is the better home for a vendor constant than a template literal: an operator can correct it when Istio changes the number, without waiting for a chart release. networkPolicy: ingress: ambientMesh: enabled: false port: 15008 redirectedProbeSources: - 169.254.7.127/32 One object under one switch, so the coupling argument survives the change instead of being undermined by it - two separate values could drift apart, which is the thing this design exists to prevent. redirectedProbeSources is a LIST because a dual-stack cluster needs BOTH of Istio's addresses at once, not one of two; that is a nameable case rather than speculation, so it does not contradict reason 4 of the issue. Istio's IPv6 counterpart is documented as the dual-stack addition, with the reason it is absent today unchanged: SingleStack IPv4, and an ipBlock of the wrong family is accepted and matches nothing. Named redirectedProbeSources, not probeSources, because the confusion risk with networkPolicy.probeSources one level up is larger than the ingress/ ingressController one: that value holds YOUR worker node CIDRs, is empty by default and means OPEN when empty; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. The distinction is written where both appear. PROVENANCE, which the previous round explained the coupling of but never attributed. 15008 is Istio's HBONE port - not IANA-registered for this, not prescribed by any protocol, unknown to Kubernetes. 169.254.7.127 is link-local (169.254.0.0/16, RFC 3927, never routed - the range one picks vendor magic out of); Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, which is what vendor constants look like; and Kubernetes knows the address in neither the NetworkPolicy API nor the kubelet - only Istio's data plane rewrites the probe source onto it. Both read for ztunnel 1.30.6. If either changes, the failure is SILENT: a pod unreachable in the mesh, or probes dropped where probeSources is non-empty, which is nowhere today. NULL-SAFE, per #99 and because #136 shipped this exact defect one value away (its NOTES.txt branch dereferenced .networkPolicy.egress directly): every read goes through a fallback reproducing the chart's own declared defaults, so a deleted object or one carrying only "enabled: true" renders the defaults rather than failing. Clearing the probe-source list restores the constant instead of removing the ipBlock, deliberately - the halves are not separable, and enabled: false is how you open nothing. THE CONSEQUENCE, and it is the point of the CI half. With the port configurable, the exception can no longer be checked against the rendered value: "whatever the value says" is design A's hole wearing design C's name, and a mistyped port would pass by sitting in the configurable field. So the expectation is PINNED IN THE TEST - AMBIENT_PORT and AMBIENT_CIDR - and assertion 7 now checks two things instead of one: that the render at chart defaults produces exactly that port and that ipBlock, and that every chart's values.yaml DECLARES the same defaults, read back through ci/yaml2json rather than by regex. A chart that drifts is red. Changing an Istio constant means touching 30 values files AND the pinned lines, and the pinned lines are what force someone to look. That block is therefore also the single place saying what we hold Istio's numbers to be, so it carries the provenance too. The renders set only enabled=true and never a port override, on purpose: the pin is a statement about the defaults we ship, not about what a release may choose - an operator overriding a value is outside this test's reach, as for every value here. The declared-defaults check is conditional on the chart offering probeSources, like the inseparability check and assertion 4: the five charts whose probe rule is never source-restricted render no ipBlock, so for them the key must be ABSENT - a value reaching no rule reads as a control that does nothing - and that absence is asserted rather than tolerated. Their schemas drop it too. No version change: nothing of this is published, the switch stays additive with its default off, and the render at defaults is byte-identical - re-diffed per chart against origin/main, not assumed. |
||
|
|
badfaa32cb
|
feat(template-chart): networkPolicy.ingress.ambientMesh, plus its checked CI exception
Gitea #143. A pod cannot be reached inside Istio's ambient mesh unless its NetworkPolicy lets ztunnel's HBONE tunnel in - ambient explicitly does not bypass policy enforcement - and 15008 is deliberately no containerPort of the application, so assertion 2 of ci/run-networkpolicy-test.sh rejected it. The switch is a named intention rather than a free port list, and it renders two things that cannot be had separately: 15008/TCP inbound, and - where probeSources is non-empty - 169.254.7.127/32 as an additional peer, because inside the mesh the kubelet's probes are redirected away from the worker node's address. Whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe: the pod never becomes Ready and the symptom reads as a broken application. That is assertion 4 of the same script, one value further along. Assertion 2 keeps its edge. It gets ONE named exception for ONE port number, gated on one value, and it is not granted in the main loop at all - which renders every chart with the value at its default, so 15008 stays an ordinary failure there. The free-port variant was design A of #143 and would have opened the assertion for all 30 charts. Assertion 7 holds the exception to five things on two renders per chart: the port and the ipBlock are absent while the switch is off, 15008 is present while it is on, 15008 is still the ONLY ingress port that is not a containerPort, and wherever the rule carrying it restricts sources it also carries the ipBlock. The last one is reachable because these renders apply ci/networkpolicy/<chart>.yaml, where probeSources is non-empty. Built into the existing script, as for #136: the exception weakens assertion 2 and belongs beside it - a separate script could go green while the exception it is paired with is wrong, with nothing connecting the two. The job already runs over every chart on every pull request. Found by running it: the policy's name comes from Chart.yaml's name, not from the directory - template-chart is called "xxx". Not proven, and said so in values.yaml, the template header and the script's header: that 15008 alone suffices comes from Istio's documentation, and the probe-source half reaches no release, because probeSources is empty in every chart here. No namespace of this cluster carries istio.io/dataplane-mode, so there is no path on which either could be measured. What is proven is the render and the assertions. |
||
|
|
1001de620a
|
feat(template-chart): the reference NetworkPolicy, on by default
The reference implementation for Gitea #5: one NetworkPolicy per workload, networkPolicy.enabled defaulting to true, plus the values block, the schema entry and the NOTES.txt section that go with it. The shape is taken from the two charts that already had one - valkey and stalwart - rather than invented, because they carry the expensive lessons: policyTypes only where there is something to restrict, namespaces always spelled out because an empty "from" allows everything, kubernetes.io/metadata.name so no foreign namespace needs labelling, POD ports and never NodePorts, egress by port because the object cannot express a hostname, and the probe path left open by default because the kubelet probes from the node's own IP. Three things are written down rather than assumed. The ingress source is measured on the cluster and is NOT what the issue body assumes: ingress-nginx is gone, the only IngressClass is traefik, and the controller is the DaemonSet rke2-traefik in kube-system - which also serves the Gateway API here, so one source covers both the Ingress and the HTTPRoute path. Egress stays coarse: with Canal (Flannel plus Calico) there is no FQDN rule, and Calico's GlobalNetworkSet would bind every chart to Calico, the same argument that kept implementation-specific objects out of #114. And a policy is only a boundary where the CNI enforces one - unenforced, it is accepted and filters nothing. All reasoning sits in Go template comments, not in "#" comments: those are part of the rendered object and travel into the cluster. Writing this header also found the second-order trap, which is now in the header itself - prose inside a template comment must not spell out the comment's own closing delimiter, or the parser ends the comment early and the remaining prose is emitted as manifest content. Part of #5 |
||
|
|
50f683776e
|
feat(cyberchef): Gateway-API as an alternative to the Ingress (pilot)
Validate Helm Charts / Detect changed charts (pull_request) Successful in 4s
Validate Helm Charts / Resource quantities (pull_request) Successful in 14s
Validate Helm Charts / Selector match (pull_request) Successful in 12s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 13s
Validate Helm Charts / Build cyberchef (pull_request) Successful in 6s
Validate Helm Charts / Trivy config cyberchef (pull_request) Successful in 18s
Validate Helm Charts / Build template-chart (pull_request) Successful in 7s
Validate Helm Charts / Trivy config template-chart (pull_request) Successful in 17s
Install-test / Install-test cyberchef (pull_request) Successful in 1m0s
Validate Helm Charts / Install-test (pull_request) Successful in 1m0s
The Gateway-API pilot of Gitea #114: one chart, the convention, the CI that holds it. The remaining 20 charts follow in their own PRs. The form, additive and off by default: httpRoute: enabled: false # counterpart to ingress.enabled parentRefs: [] # the Gateway - a platform object hostnames: [] # empty = [ingress.domain] Only HTTPRoute with parentRefs is rendered: no Gateway, no gatewayClassName, no Certificate, and no implementation-specific policy object. That subset is identical on nginx-gateway-fabric, Cilium, Envoy Gateway and Istio, which is what lets the implementation be chosen later without touching the charts again. Class and certificate move to the platform. Four guards, all against silent failures: - mutually exclusive with ingress.enabled, with a render abort naming BOTH value paths. A migration runs over a second hostname, not in parallel. - parentRefs mandatory in Gateway mode, render abort on the empty list: a route without a parent is created, is schema-valid, and routes nothing. - hostnames empty means [ingress.domain], so the hostname has one source. - the mode is a VALUE, not auto-detection: .Capabilities.APIVersions is unreliable at template time and a missing CRD fails at install time. ingress.clusterIssuer was already only read inside the Ingress template, so the "conditionally required" half of #114 needed no change in this chart - checked rather than assumed, and it has to be re-checked per chart in the sweep. CI, both halves, because neither covers the other: - the install-test installs cyberchef in GATEWAY mode against the REAL Gateway-API CRDs (standard channel v1.6.3, pinned and Renovate-tracked, applied --server-side). A schema-less stub would accept a malformed route and prove only that something was created. The Ingress path of this one chart is no longer installed; it is a built-in type that never routed in k3d anyway, and helm lint renders this chart's defaults - Ingress mode - on every PR. - ci/run-httproute-match-test.sh, over every chart on every PR: every backendRefs entry must name a Service, on a port, that the SAME render creates, and parentRefs must not be empty. The CRDs accept both of those mistakes, so the schema cannot catch them, and the only report would be a route status no controller writes in CI. What stays unprovable, and is written down instead of implied: that traffic flows. No Gateway controller runs in k3d. Minor: additive with unchanged defaults, proven by an empty helm template diff against origin/main for the default values. Part of #114 |
||
|
|
b218f64eac
|
template-chart + README: values.schema.json, gate and null-safe helpers
Closes out the schema programme (#68) by moving what the 18 charts taught into the reference implementation and the binding guidelines. template-chart: - values.schema.json as the reference schema: additionalProperties false on every chart-owned level, pod-spec passthrough left open, and the reasoning inline in the descriptions so a copy carries it along. - defaultedDict and subBlock plus the per-block fallbacks for both security contexts, the three probes and resources, wired into both the Deployment and the StatefulSet. - the reference itself violated guideline 1: its env block reads ingress.domain, so with the Ingress gated off it rendered an empty value. That read is now required(), and the comment says why - the template is what people copy, so it has to teach the guard, not the hole. README, new section "Values that must not lie" with the five rules: 1. a value read outside its obvious template needs its own guard (matrix-synapse published '{"m.server": ":443"}' to federation without it, rendering cleanly and reporting healthy) 2. probes, contexts and resources through the null-safe helper, never behind an "if" - a guard removes the probe instead of showing a null 3. no liveness on an external dependency where a dependency-free path exists, otherwise document why on the helper 4. a fallback reproduces THIS chart's defaults, not the repo standard - calibre's readOnlyRootFilesystem: false is what keeps the runtime uid at 1000 instead of 911 and existing volume data readable 5. a probe default carries its context - port name, Host header, exec command - or the probe vanishes (teamspeak) or gets 403 (patchmon) Plus the two mechanics that are easiest to lose: null reaches schema validation only for keys WITHOUT a chart default (which is why the helper and the ["object","null"] types each cover a case the other cannot), and schemas must be validated against real bundle values - that step caught a draft of mine that would have broken three live paperless releases. CLAUDE.md: the new-chart checklist now names the schema and the adaptation step, since the copied file is chart-specific and wrong until it is adapted. freesailarr is not covered: it is not on main yet (#59). |