Repository navigation
Mesh 3 policy migration - #7211
Open
justindavies wants to merge 93 commits into
Open
justindavies wants to merge 93 commits into
justindavies wants to merge 93 commits into
Conversation
✅ Deploy Preview for kongdeveloper ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
fabianrbz
force-pushed
the
release/mesh-3
branch
from
September 21, 2026 15:44
1e9bfde to
d41c003
Compare
MeshTrafficPermission was documented as two pages, meshtrafficpermission
and meshtrafficpermission_experimental, both declaring the same policy
name. They described two mutually exclusive spec formats: the 2.x `from`
array with an `action`, and the SPIFFE ID matcher form under `rules`.
Kong Mesh 3 accepts only the second. Verified against a live 3.0 control
plane: applying the `action: Allow` form is rejected with "at least one of
'allow', 'allowWithShadowDeny', 'deny' has to be defined", and `from` was
removed as a policy targetRef concept, with /_resources now reporting
hasFromTargetRef: false for every policy type. So the page labelled
experimental documented the only supported syntax, and the unlabelled one
documented a form the control plane refuses.
There is now a single meshtrafficpermission page covering the SPIFFE ID
form, built from the experimental page's content, plus:
- the evaluation order, and why a deny cannot be overridden by an allow
- the spiffeID and sni matchers, with Exact and Prefix, and how a
prefix addresses a whole namespace
- allowWithShadowDeny as the way to validate a rule before enforcing it
- targetRef.sectionName for narrowing to one port
- zone proxies, carried over from the 2.x page
- a section on upgrading from 2.x, mapping from/action onto rules
Field names follow the CRD: `spiffeID`, capital ID, as in
api/common/v1alpha1/match.go. A live control plane also accepts
`spiffeId`, since Go's JSON decoder matches field names case
insensitively, but that spelling is not canonical.
Four examples replace the three carried over, adding a shadow-deny one.
The v2 snapshot keeps both 2.x pages, so 2.x readers still have the
`from` documentation. Their canonical_url entries now point at the single
latest page.
The top-level experimental stub is removed, with redirects for its index
and reference URLs. Three example redirects that Fabian added pointing at
the v2 snapshot now point at the migrated examples instead, since those
exist at latest. The legacy service-allow-requests example redirect still
points at v2, because that form is not carried forward.
MeshTrust and MeshIdentity linked to the experimental URL and now link to
the consolidated page.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
"Reach for it when you need to..." addresses the reader as a friend offering advice. The reader is looking for information about the policy, not counsel. State what the policy is for instead: Use it to trace a failing request across services, ... Same sentence, same content, no change in meaning. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The matcher table claimed both spiffeID and sni take a type of Exact or Prefix. That holds for spiffeID; sni accepts Exact only. SNIMatchType is a single-value enum in api/common/v1alpha1/match.go (+kubebuilder:validation:Enum=Exact), and ValidateMatch rejects anything else with "unrecognized type, supported values are: Exact". It also requires the value to be a DNS-1123 subdomain, where spiffeID requires a parseable SPIFFE ID. The table now carries a type column per matcher rather than one sentence covering both. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The evaluation order was stated correctly but its consequence was not. In 2.x the last matching rule won, so the common pattern was a mesh-wide deny-all followed by narrower allow rules that opened specific paths. That inverts in 3.0. RBACConfigurer.createMatcher emits every deny matcher from every merged policy before any allow matcher, and Envoy stops at the first match, so a blanket deny matches all clients and no later or narrower allow can re-open it. Carrying the 2.x pattern forward therefore denies everything. The pattern is also unnecessary: the matcher's OnNoMatch action is RBAC_DENY, so a mesh with no MeshTrafficPermission already allows nothing. Only allow rules need writing. Says so in both places a reader meets the question: under the evaluation order, and in the section on upgrading from 2.x. This also confirms the default-deny statement in the page opening, which had been carried over from the previous page rather than verified. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The zone proxy section presented 10001 and 10002 as the sectionName values for zone ingress and egress, carried over from the 2.x page. They are neither constants nor listener names. sectionName matches Dataplane.networking.listeners[].name. Per api/mesh/v1alpha1/dataplane.proto, that field is optional and "if unset, the port value is used as the name (as a string)". So the value depends on the listener's port, or on an explicit name where one is set. Neither number appears anywhere in the Kong Mesh 3 source: the only 10001 is FirstOutboundPort in a test builder. 10001 and 10002 are the chart defaults (deployments/charts/kuma/values.yaml, ingress.service.port and egress.service.port), so they are usually right on a Helm install and wrong anywhere the ports were changed. Now explains what sectionName matches, gives the defaults as defaults, and shows how to read the listener names off the Dataplane. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds the latest MeshRetry page, with four examples, and points the v2
snapshot's canonical_url entries at it.
Checked against the v3 source rather than carried over from the 2.x page,
which turned up one thing worth a warning on the page:
retryOn accepts `5xx`, lower case. `5XX` is rejected by the control
plane as "unknown item '5XX'", because validateHTTPRetryOn switches on
the constants exactly and All5xx is "5xx". `5XX` nevertheless appears
as the example value in both the CRD
(pkg/plugins/policies/meshretry/k8s/crd/kuma.io_meshretries.yaml) and
the OpenAPI schema, and in the prose of the 2.x page. There is no
kubebuilder enum on HTTPRetryOn, only an example, so a Kubernetes apply
passes schema validation and the control plane rejects it afterwards.
Every other named condition is upper camel case, which makes the
exception easy to trip over.
Other facts taken from source:
- targetRef accepts Mesh or Dataplane; to[].targetRef accepts Mesh,
MeshService, MeshExternalService, MeshMultiZoneService or
MeshHTTPRoute, per the TopLevelTargetRefKind and
OutboundTargetRefKind enums.
- gRPC retryOn is a closed kubebuilder enum, unlike the HTTP list.
- TCP takes only maxConnectAttempt.
- Defaults: numRetries 1, backOff.baseInterval 25ms, backOff.maxInterval
ten times baseInterval, rateLimitedBackOff.maxInterval 300s,
Predicate.updateFrequency 2, hostSelectionMaxAttempts one reattempt.
- At least one of tcp, http or grpc is required in a `default`.
The upgrade section records the two 3.0 changes that affect an existing
policy: MeshSubset and MeshServiceSubset are gone as selectors, and
header matches no longer materialize `type: Exact` into the stored
resource, per UPGRADE.md.
Three redirects that pointed latest example URLs at the v2 snapshot now
point at the migrated examples.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every MeshRetry example was rejected by a Kong Mesh 3 control plane. Two
faults, both from copying the shape of the 2.x examples:
spec.to[0].targetRef.labels (): must be set when kind is MeshService
spec.to[0].default.http.rateLimitedBackOff.resetHeaders[0].name ():
in body should match '^[a-z0-9!#$%&'*+\-.^_\x60|~]+$'
OutboundTargetRef carries only kind, labels and sectionName. It has no
name field, so `name: backend` selected nothing and labels is required. A
MeshService is selected the way UPGRADE.md selects one, by
kuma.io/display-name. The 2.x examples used name, namespace, sectionName
and a _port field, none of which apply.
HeaderName is constrained to lower case by a kubebuilder pattern, so
Retry-After and X-RateLimit-Reset had to become retry-after and
x-ratelimit-reset.
Reading the struct was not enough to catch either: the OutboundTargetRef
fields were in front of me and I wrote `name` anyway, and the pattern
lives on the HeaderName type rather than at the point of use. Both
surfaced only on apply.
All four examples/ files and all three policy_yaml blocks in the page now
apply cleanly to a 3.0 control plane. The MeshTrafficPermission page's
four blocks and four examples were re-checked the same way and needed no
change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
"Two pieces do the work:" and "Three pieces do the work:" personify configuration fields and tell the reader nothing about what follows. Replaced with "What each field does:", which names the list. Three pages: the phrasing originated in the MeshAccessLog overview and was carried into MeshTrafficPermission and MeshRetry when those were written. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds the latest MeshCircuitBreaker page, with four examples, and points
the v2 snapshot's canonical_url entries at it.
Checked against the v3 source, which surfaced two silent-data-loss traps
that both get a warning on the page:
spec.from is removed, and dropped silently. A policy using from
alongside to or rules is accepted and the from configuration has no
effect on inbound traffic; a policy where from was the only field set is
rejected, because the spec then has neither to nor rules. Per
UPGRADE.md, from maps onto rules, and a from entry targeting kind: Mesh
becomes a single catch-all rule.
healthyPanicThreshold moved here from MeshHealthCheck, and un-migrated
settings are dropped silently. It now lives at
to[].default.outlierDetection.healthyPanicThreshold. On Kubernetes the
old field is pruned by CRD validation and on Universal it is discarded
during deserialization, so nothing reports it and the cluster reverts to
Envoy's 50% default.
Other facts taken from source:
- This policy has both to and rules, unlike MeshRetry (to only) and
MeshTrafficPermission (rules only). At least one is required, and each
default needs at least one of connectionLimits or outlierDetection.
- to[].targetRef accepts Mesh, MeshService, MeshExternalService or
MeshMultiZoneService. It does not accept MeshHTTPRoute, which MeshRetry
does: circuit breaking applies to a destination, not to a route.
- rules applies to all inbound traffic, with no L7 matching, so a single
catch-all entry is the only available shape.
- detectors is required whenever outlierDetection is set and needs at
least one of its five detectors.
- maxEjectionPercent defaults to 10%, and at least one endpoint is
always ejectable regardless of it.
- healthyPanicThreshold defaults to 50%; 0 disables panic mode.
All four examples and all three policy_yaml blocks were applied to a live
Kong Mesh 3 control plane and accepted, then deleted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A reader coming from MeshTrafficPermission to MeshCircuitBreaker asks why
one matches clients by SPIFFE ID and the other names a MeshService. The
pages carried both facts and explained neither.
Each page now opens its "Where a policy applies" section with the three
selectors and what each answers:
spec.targetRef which proxies the policy is installed on
spec.to[].targetRef which destination, for traffic the proxy sends
spec.rules[] which client, for traffic the proxy receives
Followed by the reason they differ: a destination is something the proxy
chooses to call, so it is named directly; a client is remote and asserts
its own identity, so a name or label would be the caller's own claim about
itself, and it is matched on the SPIFFE ID MeshIdentity issued instead.
That is also why v3 dropped `from` with MeshServiceSubset from
MeshTrafficPermission.
The four shapes differ more than a reader would guess, so each page states
its own rather than implying a common one:
MeshTrafficPermission rules only, matching clients
MeshRetry to only
MeshAccessLog to and rules, rules can match clients
MeshCircuitBreaker to and rules, but rules cannot match at all —
L7 matching for inbound circuit breaking is not
implemented, so it is catch-all only
Kinds per field follow the cross-policy table in UPGRADE.md: only
MeshAccessLog, MeshLoadBalancingStrategy, MeshRetry and MeshTimeout accept
MeshHTTPRoute in to[], which MeshCircuitBreaker rejects.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The previous commit explained the three selectors on each policy page,
which duplicated a shared concept four times and would have repeated it
across every remaining policy.
Adds /mesh/policy-targeting/, covering:
- the three selectors and what each answers
- why outbound names a destination and inbound matches an identity: a
destination is something the proxy chooses to call, while a client
describes itself, and a label would be the caller's own claim. This is
why 3.0 removed `from` and MeshServiceSubset from
MeshTrafficPermission.
- what each field accepts, including the cross-policy rules from
UPGRADE.md: MeshHTTPRoute is accepted in to[] only by MeshAccessLog,
MeshLoadBalancingStrategy, MeshRetry and MeshTimeout; MeshRateLimit
and MeshFaultInjection accept Mesh only, and only when the top-level
targetRef selects a gateway; MeshSubset, MeshServiceSubset and
MeshGateway are rejected in to[] everywhere.
- the spiffeID and sni matchers, with sni accepting Exact only.
Each policy page now states only what is true of itself and links to the
reference. The per-policy facts that differ are kept where a reader needs
them: MeshTrafficPermission has no `to`, MeshRetry has no `rules`,
MeshCircuitBreaker allows both but its `rules` cannot match a client.
Placed in app/mesh/ alongside the other standalone mesh references, since
/mesh/policies/ is a generated list page.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every policy page now carries "How policies select traffic" as its first related_resources entry, so the shared concept is the first thing offered alongside a policy. MeshAccessLog had no related_resources block and now has one. Also fixes the tags on the new page: `policies` and `targetref` are not in the frontmatter schema enum, which failed make validate-frontmatters. Replaced with `policy`, `service-mesh` and `traffic-control`, which are. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
"This is the part that looks inconsistent and is not." performs rather than states. The heading above it already named the subject, so the sentence carried no information and existed only for effect. Deleted. Two aphorisms replaced with the mechanism they were standing in for: "access control decided on a self-asserted tag was not access control" becomes "those selectors matched an inbound client on tags the client set itself". "sends traffic to all endpoints regardless of health, on the grounds that a degraded endpoint beats no endpoint" becomes "resumes sending traffic to all endpoints, ejected ones included, rather than failing the requests". This also makes healthyPanicThreshold's meaning explicit: the percentage of healthy endpoints below which panic mode starts. "Two differences from other policies are worth knowing" editorialised about the content and is now "This policy differs from others in two ways". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds the latest MeshTimeout page, with four examples, and points the v2 snapshot's eight canonical_url entries at it. The 2.x page had seven examples covering variations of the same two cases; the latest page has four, and the redirects map each old URL onto the one that covers it. Two restrictions came out of the source and are both confirmed against a live 3.0 control plane: `to` and `rules` are mutually exclusive, unlike MeshCircuitBreaker where both may be set together. A policy defining both is rejected with "fields 'to' must be empty when 'rules' is defined", so inbound and outbound timeouts need separate policies. A rule matching on spiffeID may set only http.requestTimeout and http.streamIdleTimeout. The other five fields are rejected with "can't be specified when matches contain spiffeID because this field cannot be conditioned on source identity". The page explains why: those two act on a request or stream, which belongs to one client, while connectionTimeout, idleTimeout, requestHeadersTimeout, maxStreamDuration and maxConnectionDuration act on a connection or listener, which is shared. The defaults are documented, since a mesh already has timeouts and a policy changes them rather than introducing them. Kuma creates two MeshTimeout resources per mesh, from pkg/defaults/mesh/meshtimeout.go and pkg/plugins/policies/core/defaults/consts.go: connectionTimeout outbound 5s inbound 10s idleTimeout outbound 1h inbound 2h http.requestTimeout outbound 15s inbound 0, disabled http.streamIdleTimeout outbound 30m inbound 1h http.maxStreamDuration outbound unset inbound 0, disabled Inbound is twice outbound, so a receiving proxy does not time out a request before the caller does, and the inbound request timeout is disabled for the same reason. A mesh created with skipCreatingInitialPolicies covering MeshTimeout has neither default. The upgrade section covers `from` becoming `rules`, the removed targetRef kinds, and the Universal inbound that loses HTTP timeouts when its protocol is declared only through the kuma.io/protocol tag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds the latest MeshHealthCheck page, with four examples, and points the
v2 snapshot's canonical_url entries at it.
The healthyPanicThreshold removal is verified live, including the part
that makes it dangerous. A policy setting the field is accepted rather
than rejected, and reading it back shows the field gone:
$ kongctl create mesh -f hc-with-panic-threshold.yaml
created
$ kongctl get mesh meshhealthchecks hc-panic-probe -o json --jq '.spec.to[0].default | keys'
["http", "interval", "timeout"]
So nothing reports the loss, and the cluster reverts to Envoy's 50%
default. The page says so, and points at
MeshCircuitBreaker.to[].default.outlierDetection.healthyPanicThreshold.
The page opens by distinguishing this policy from MeshCircuitBreaker,
since they reach the same outcome differently: probes here are scheduled
and independent of real requests, which is active checking, while
outlierDetection judges the traffic already flowing. A destination with
little traffic is the case for active checks, because there may not be
enough requests to judge it by.
Facts taken from source:
- to only, no rules: a health check is something a caller performs.
to[].targetRef accepts Mesh, MeshService, MeshExternalService or
MeshMultiZoneService, and not MeshHTTPRoute, since a probe addresses
an endpoint rather than a route.
- At least one of http, tcp or grpc is required.
- Defaults: interval 1m, timeout 15s, unhealthyThreshold 5,
healthyThreshold 1, noTrafficInterval 60s, reuseConnection true, HTTP
path /, expectedStatuses 200 only, and statuses limited to 100-599.
- Protocol selection takes the most specific configured protocol, and
disabled: true falls back to the more general one.
- TCP receive blocks match fuzzily but in order; an empty receive makes
the probe connect-only.
The page also notes what the default interval and threshold mean together:
an endpoint failing every probe is removed about five minutes after it
starts failing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rewrite the policy page against the v3 source. The `from` array is gone; clients are matched with `rules[].matches` on `spiffeID` or `sni`, and `targetRef.kind` is limited to Mesh and Dataplane. Document the `to[]` restriction, which is verified in the validator and against a live v3 control plane: `to` is rejected outright unless `targetRef.kind` is Mesh, and then only accepts `kind: Mesh`. The restriction exists because a fault is produced by the proxy answering the request, so it is configured inbound; the `to` form covers gateways. Also cover the three fault types with their validated ranges, and the compounding of percentages down the fault list. Four examples replace the three v2 ones, all applied to a v3 control plane and accepted. v2.yml canonical URLs and the latest -> v2 example redirects now point at the migrated pages. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rewrite the policy page against the v3 source, and cover the behavioural changes that matter more than the schema ones. Verified in source and against a live v3 control plane: - `to[].targetRef` accepts only MeshService, MeshExternalService and MeshMultiZoneService. `Mesh` is rejected with `value 'Mesh' is not supported`, so a route always names a destination. - `to[].hostnames` is in the schema but unimplemented, and rejected with `must not be defined`. Same for `urlRewrite.hostToBackendHostname`. - `replacePrefixMatch` is rejected unless every entry in the rule's matches uses a PathPrefix path. - `backendRefs[].kind` no longer accepts MeshServiceSubset, and `tags` is gone from the schema. - A MeshMultiZoneService backendRef requires `port`. - A PathPrefix other than `/` is rejected if it ends in a slash. Documented from source, not exercised at runtime: the 404 for a request matching no rule, the 500 for a rule whose backendRefs all fail to resolve or name a missing port, the 503 for an all-zero-weight list, and the Gateway API precedence order from CompareMatch. `targetRef.kind` is Mesh or Dataplane only; real resources are selected by labels. Four examples replace the three v2 ones, all applied to a v3 control plane and accepted, along with the page's inline block. v2.yml canonical URLs and the latest -> v2 example redirects now point at the migrated pages. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The section was six warning callouts with no order or reasoning between them. Replace it with an ordered walkthrough: six steps, each saying what changed, what breaks if it is skipped, and before/after YAML where the rewrite is not obvious. The steps are ordered by what the change costs to miss, and the opening paragraph draws the distinction that decides that: the selector rewrites are rejected on apply, while the routing changes pass validation and change where traffic goes. Two facts verified live while writing it. A targetRef carrying name and namespace instead of labels is rejected with `labels (): must be set when kind is MeshService`, and a legacy top-level kind with `in body should be one of [Mesh Dataplane]` -- so both are loud, not silent. The 3.x before/after selector, including `k8s.kuma.io/namespace`, was applied to a v3 control plane and accepted. Also dropped UPGRADE.md's claim that MeshServiceSubset survives as a backendRefs kind. The validator restricts backendRefs to MeshService, MeshExternalService and MeshMultiZoneService, and a live control plane rejects MeshServiceSubset there, which agrees with the same document's other section on it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Same treatment as MeshHTTPRoute: ordered steps with before/after YAML, the exact validation error where a change is rejected, and an explicit note where it is not. The order in each is what the change costs to miss, so the silent ones come first. Checking the source rather than the existing text turned up several things the callout lists had wrong or missing. A top-level `kind: Dataplane` does not require `labels`. ValidateTargetRef requires them for MeshService, MeshHTTPRoute, MeshExternalService and MeshMultiZoneService, and for Dataplane requires nothing, so a 2.x `kind: Dataplane` with `name` is accepted, `name` is dropped, and the policy widens from one proxy to every proxy in the mesh. Verified live: a MeshTrafficPermission written that way was created and read back as a bare `kind: Dataplane`. That is a security change on MTP and a silent one everywhere, so it is now a warning on all seven pages. MeshRetry's note about `type: Exact` no longer being a schema default was wrong: HeaderMatch.Type still carries `+kubebuilder:default=Exact`. Replaced with the constraint that actually bites, the lower-case-only HeaderName pattern, which rejects a 2.x `Retry-After`. Exact errors and behaviours confirmed against a v3 control plane: - MTP with only `from`: `spec (): policy must define rules`. - MeshTimeout, MeshCircuitBreaker, MeshAccessLog with `from` alongside `to`/`rules`: created, and `from` absent when read back. With `from` alone: `spec (): at least one of 'to' or 'rules' has to be defined`. - MeshHealthCheck with `healthyPanicThreshold`: created, field absent when read back. - MeshAccessLog with `openTelemetry.endpoint`: `openTelemetry.backendRef (): must be defined`. The backendRef form was applied and accepted. - MeshRetry `resetHeaders[].name: Retry-After`: rejected by the HeaderName pattern. - Legacy top-level kinds: `in body should be one of [Mesh Dataplane]`. Added from source, not previously covered: which MeshTimeout fields sit under `http` and are lost when a Universal inbound has no protocol (connectionTimeout and idleTimeout are not); that MeshCircuitBreaker's to[] does not accept MeshHTTPRoute; the MeshProxyPatch circuitBreakers merge change; MeshTrafficPermission's MeshIdentity prerequisite, the legacy TrafficPermission becoming inert, and the three removed control plane settings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rewrite the page against the v3 source, with a migration guide in the same shape as the other policies. Verified in source and against a live v3 control plane: - `to[].targetRef` accepts only MeshService, MeshExternalService and MeshMultiZoneService. `Mesh` is rejected, so the 2.x gateway form (targetRef: MeshGateway with to[].targetRef.kind: Mesh) has no equivalent -- name the destination instead. - `backendRefs` is required: an empty or missing list is rejected with `backendRefs (): must be defined`. This differs from MeshHTTPRoute, where omitting it falls back to the destination. - `to` needs at least one entry, and a `to` entry accepts at most one rule, since a TCP rule has no matches to distinguish a second one by. - backendRefs no longer accept MeshServiceSubset. - Real resources are selected by labels only. From the xDS path, not exercised at runtime: an unresolved backendRef is dropped from the split rather than answered with an error, because a TCP proxy has no status code to answer with, and where none resolve the client gets no outbound listener for that destination at all (generateFromService returns nothing for an empty ref list). That is a different failure from MeshHTTPRoute's 500. Also from source: MeshHTTPRoute wins whenever the destination is HTTP based and any MeshHTTPRoute targets it, whatever either route configures (getBackendRefs returns nil in that case). Four examples replace the two v2 ones, all applied to a v3 control plane and accepted, along with the page's inline block. v2.yml canonical URLs and the latest -> v2 example redirects now point at the migrated pages. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rewrite the page against the v3 source, with a migration guide in the
same shape as the other policies.
Verified in source and against a live v3 control plane:
- `to[].targetRef.kind: MeshHTTPRoute` accepts only `hashPolicies`.
`loadBalancer` and `localityAwareness` are rejected with `field is not
allowed when targetRef.kind is MeshHTTPRoute, only hashPolicies is
supported`.
- `localityAwareness.crossZone` is rejected on any kind but
MeshMultiZoneService, with `crossZone is only supported when
targetRef.kind is MeshMultiZoneService`.
- `affinityTags` weights are all-or-nothing: `all or none affinity tags
should have weight`.
- A failover `to.type` of Any or None must have no `to.zones`; Only and
AnyExcept must have some.
- `loadBalancer.ringHash.hashPolicies` is accepted and silently dropped,
not rejected -- UPGRADE.md left this open ("may be rejected by
validation or have those fields pruned"). Reading the policy back shows
an empty `ringHash: {}`, so the algorithm stays and has nothing left to
hash. That is now the first step of the guide, with a warning, since
requests that were pinned to an endpoint quietly stop being pinned.
The v2 page documents `hashFunction` as `XX_HASH` / `MURMUR_HASH_2`,
which the control plane rejects: `in body should be one of [XXHash
MurmurHash2]`. The enum has been those two values since at least 2.14, so
this is a docs error rather than an upgrade change, and the new page does
not present it as one.
From source, not exercised at runtime: locality awareness is on unless
disabled (isLocalityAware is true when localityAwareness is unset), and
`localZone`/`crossZone` apply regardless of `disabled`, because
atLeastOneLocalityGroup is evaluated independently of it. Unset
affinityTag weights are generated as 9*10^(n-i-1), which is the 90/9/1
split.
The 2.x Zone Egress caveats are dropped rather than carried over:
ZoneEgress and ZoneIngress are removed in v3, so the restriction they
imposed on the top-level targetRef is gone with them.
Five examples replace the five v2 ones, all applied to a v3 control plane
and accepted, along with the page's inline block.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The ten policy pages each carried their own "Upgrading from 2.x" section, which put the migration in front of readers who were there to configure a policy, and repeated the shared changes ten times. Move the content to app/mesh/migrate-policies-to-3.md and link to it from each policy's related resources, second after the targeting page. Consolidating removed real duplication rather than just relocating it. Stated once on the new page instead of per policy: - Legacy top-level targetRef kinds, with the warning that a `kind: Dataplane` carrying `name` is accepted and widens the policy to the whole mesh. That was repeated on eight pages. - Real resources selected by labels, and the contrast that the four real-resource kinds require labels while Dataplane does not. - The `from` to `rules` move, now a table of which policies reject a stored `from` and which accept and drop it, since that differs. - The MeshHTTPRoute 404, which reached four other policy pages because they can attach to a route. - The Universal inbound protocol change, which listed a different subset of lost L7 filters on each page it appeared on. - The legacy policies going inert, previously split between the MeshTrafficPermission and MeshTCPRoute sections. - MeshServiceSubset backendRefs and "a route names a destination", shared by the two route policies and previously written out twice. Policy pages drop from 3080 to 2187 lines; the migration page is 585. No content is lost: every verified error string and behaviour from the per-policy sections is carried over. Front matter validates, the page builds, all internal anchors and outbound links resolve, and vale is clean. The latest mesh tree has no navigation index yet (only app/_indices/mesh/v2.yaml exists, and policy-targeting.md is not registered anywhere either), so the page is reachable by link and will need adding to the sidebar when that index lands. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rewrite the page against the v3 source. Upgrade notes go to /mesh/migrate-policies-to-3/, not onto the policy page. The `to` and `rules` split is stricter than any other policy, and is verified live in all four combinations: - `targetRef: Dataplane` accepts `rules`; `to` is rejected with `spec.to (): must not be defined`. - `targetRef: Mesh` accepts `to`; `rules` is rejected with `spec.rules (): must not be defined`. - Both together: `field 'to' must be empty when 'rules' is defined`. - Neither: `at least one of 'to' or 'rules' has to be defined`. That leaves no way to write a mesh-wide inbound limit as `kind: Mesh`. A bare `kind: Dataplane` with no labels does it instead, which was also applied and accepted. Both facts are on the page and in the migration guide, since a 2.x policy carrying both fields has to become two. Also verified live: a rule whose matches contain a spiffeID cannot carry `local.tcp` (`can't be specified when matches contain spiffeID because this field cannot be conditioned on source identity`), because the identity comes out of the TLS handshake that a connection-level limit is counting rather than inspecting; and an interval of 10ms is rejected with `must be greater than: 50ms`. From source: the limit is a local token bucket enforced per proxy, so a destination's effective ceiling is the limit times its replica count -- worth stating, since MeshGlobalRateLimit does not exist on v3. The default rejection status is 429, confirmed by the generated listener (`status: code: TooManyRequests` when `onRateLimit.status` is unset). Five examples replace the two v2 ones, all applied to a v3 control plane and accepted, along with the page's four inline blocks. MeshRateLimit is added to the shared `from` table on the migration page as one of the policies that accepts and silently drops a stored `from`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nges Rewrite the page against the v3 source, with its upgrade notes on /mesh/migrate-policies-to-3/. Re-fetching kuma master first turned up sixteen UPGRADE.md sections added since I extracted it earlier in this work, two of which land on pages already written: - `MeshHTTPRoute`/`MeshRetry` header matches and `MeshHTTPRoute`/`MeshTCPRoute` backend refs no longer declare schema defaults for `type` and `weight`. Both kubebuilder annotations are gone from api/common as of today. An earlier commit removed a MeshRetry note saying exactly this, on the grounds that the annotation was still present -- which was true when I checked and is no longer. Restored as a shared note, now covering weight too. Behaviour is unchanged; only what the API echoes back differs. - OpenTelemetry `backendRef` backends always export through kuma-dp, and the two settings that made Envoy export directly are removed. Added to the MeshAccessLog section, and it applies to MeshTrace and MeshMetric when those are written. MeshPassthrough itself has two changes from commits dated today: - A `Domain` match is resolved by the sidecar and pinned to a port instead of building an ORIGINAL_DST cluster. The old behaviour let a workload dial any address, present an allowed domain, and reach that address through the policy. A non-wildcard `Domain` now requires `port`; a stored match without one stops applying, and where it was the policy's only match nothing is left to allow. - Two matches resolving to the same Envoy filter chain are rejected on apply, where such a policy used to be accepted and Envoy then rejected the whole listener. Verified live: domain with `tcp`, partial wildcard, wildcard with an L7 protocol and no port, `mysql` without a port, a duplicate value on one port and protocol, and an invalid CIDR are all rejected with the strings quoted on the page. Distinct chains are accepted -- `tcp` and `tls` on one address, two `http` domains on one port -- as is one domain repeated across two ports. The domain-requires-port check could not be exercised: the Konnect control plane was created 2026-09-07 and the commit landed 2026-09-11, so it still accepts a portless domain. Documented from source and UPGRADE.md rather than from the live response. Also from source, replacing the v2 page's weaker wording: without transparent proxying the plugin is skipped entirely and the proxy is given the warning `policy doesn't support proxy running without transparent-proxy`; and `passthroughMode` defaults to Matched, where the v2 page says None. Four examples replace the three v2 ones, all applied to a v3 control plane and accepted, along with the page's two inline blocks. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two changes. Each policy page's "Migrate policies to Kong Mesh 3" link now points at that policy's own section rather than the page root. A deep link that skipped the shared changes would have been worse than no deep link, because for some policies the shared changes are all of them, so every policy section now opens with a line naming the shared changes that apply to it and linking to each. MeshTimeout had no section of its own for that reason -- every change affecting it is shared -- so it has one now that says so and lists them. All twelve deep links resolve against the built page. MeshGlobalRateLimit is dropped rather than migrated: it does not exist on v3. The latest stub is deleted, so /mesh/policies/meshglobalratelimit/ and its generated reference page are gone. The release map loader takes either `status: pending` or a non-nil canonical_url, with no state for "removed", so the three v2 entries now point at the migration page -- which is the successor to those pages, in that it explains where the policy went. The dead latest URLs redirect to the removal note. That note comes from the vendored kong-mesh UPGRADE.md, which already documents the removal: the policy and its control plane support are gone along with the rate-limit service the Helm chart deployed, so the `ratelimit.*` / `global.ratelimit.*` values, the `KMESH_GLOBAL_RATE_LIMIT_*` env vars and `kmesh.globalRateLimit` all need removing before upgrading. A leftover resource becomes inert rather than rejected, and the CRD outlives the upgrade because Helm does not delete CRDs. The note also says plainly that there is no replacement for a fleet-wide limit, since MeshRateLimit is local to each proxy. Re-fetched all three repos first: kuma is at 89cc0192f6 and added no new UPGRADE.md sections since the last catch-up. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…handle ai_gateway_policies
…st the konnect-beta provider schema
…ypes; record validate baseline
The stubbed provider-schema tests ignore their input, so a regression that drops the provider pin or the stub mesh resources would keep the suite green and only drift in warn mode. Add a stub that inspects the assembled main.tf: validate fails unless it declares the pinned kong/konnect-beta version, the provider source, and the stub konnect_mesh and konnect_mesh_control_plane resources.
Escape backslash and double quote in quoted string values, and double a literal template interpolation start in quoted strings and heredoc bodies so no published block performs HCL interpolation.
The provider types jsonPatches[].value as a string holding JSON text, so a scalar patch value now renders as its JSON encoding in quotes on the Terraform tab. The source example keeps the raw value, so the Kubernetes and Universal tabs stay unchanged.
The pinned provider models a fixed set of fields (percentage,
standard_deviation_factor, active_request_bias, healthy_panic_threshold,
target_port, and client/overall/random under sampling) as int-or-string
wrapper objects. The Terraform tab now renders them as { integer = ... }
for numeric values and { str = "..." } for strings; other fields keep
their plain rendering.
…ce example The mis-indented networking key left address as a sibling, which the validator reports as a gating finding.
The pinned provider has no resource for the legacy ExternalService policy and types the structured openTelemetry.body as a plain string, so no valid Terraform block exists for the two v2 external-services examples and the two logging-to-multiple-backends examples. Set tools: [kubernetes, universal] on them so the site stops publishing invalid HCL.
…by default The default --terraform-validate mode flips from warn to gate; warn stays as an explicit opt-in. Delete BASELINE.md: the gate re-verifies the baseline on every run, so a findings-record file holds no remaining content. Record the standing --skip external-services@v2 (the legacy ExternalService policy has no top-level spec, so its Kubernetes tab renders a null spec, a known renderer defect out of scope here) and the rule that a provider pin bump re-dumps the provider schema before the gate run. The workflow command stays unchanged; it now gates.
…ecks The run restructures into three passes (Kubernetes and Universal block checks, terraform grammar, provider-schema validation), each printing a start line completed with a done marker and block count when the pass ends. The provider-schema pass prints no line in off mode. Page HTML is parsed once and the extracted blocks cached, so no page is read twice. The findings and summary lines stay unchanged.
Exact-text eq per the design's testing seam, so a wrapper rendered in the wrong position or with the wrong structure fails the spec, not only a wrong attribute line.
…ed page The first full gate run over the overview and other mesh page classes found three Terraform-tab renderer defects: a source configuration with top-level labels produced a duplicate labels attribute, HostnameGenerator (a control-plane-scoped resource) received a mesh attribute the pinned provider does not define, and a top-level status reached the block although every konnect_mesh_* resource models it read-only. The renderer now renders one merged labels attribute, omits the mesh attachment for control-plane-scoped types, and drops the read-only status. Also quote the boolean label and tag values in the hostnamegenerator and performance-tuning examples, drive finding ordinals from the source instance count, add tests for the no-tag, skip, and ordinal rules, and trigger the validator workflow for app/mesh changes.
* initial style and structure fixes * Update application-ingress.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Initial commit of Mesh v3 policies
Description
Fixes #7146
Preview Links
Checklist
descriptionentry in frontmatter.