Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,11 @@ admits it and names no consumer, so a consumer's edges change its own share
alone ([chapter 16](spec/v1/16-dependencies.md#the-consumer-label),
[0035](docs/adr/model/0035-network-policy-is-default-deny-and-render-only.md)).

**Member label**: the label every gate member's pods carry, which the
Release Gate's policy reaches on any port while the member's own policy admits
the gate on one. Neither names the other's Processes, so a member added changes
its own share alone ([chapter 16](spec/v1/16-dependencies.md#the-member-label)).

**Tier**: where the edge terminates: five facts, `audiences`, `listener`,
`certificates`, `forwardAuth`, `proxiedBy`, plus the Traefik Application that is its proxy. A
route's audience is the only way it reaches a tier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ that says who may. The migration
runner carries the label of its datastore's Surface too, so no datastore's
policy names a migration identity.

**The Release Gate reaches its members by a label too.** The gate requests each
member's new copy itself during analysis, so each member admits the gate, and
the gate reaches every pod carrying the
[member label](../../../spec/v1/16-dependencies.md#the-member-label), on any port,
the member's own ingress naming the one port it admits. Naming every member in
the gate's policy would make each new gated Application re-render the delivery
Project; admitting every pod the model manages would let the gate reach
datastores it never asks.

**Whether a delivered render applies the policy set is open.** Flux applies each
Project's share of the render ([0050](0050-delivery-is-part-of-the-model.md)),
and a policy file listed in an applied kustomization is enforced on first sync.
Expand All @@ -100,6 +109,7 @@ proposal awaiting the maintainer's decision.
| Wait for the CNI before rendering | nothing unenforced in the tree | puts a lab evaluation on the model's critical path |
| Name each consumer in the provider's policy | a policy that lists exactly who reaches it | a consumer's edge re-renders, re-pins and re-releases the provider, so one Project's change moves another |
| Admit a consumer by a label on its Namespace | one label per Project, not per pod | every pod in the namespace reaches the Surface, not only the Process that declared the edge |
| Let the gate reach every pod the model manages | no member label | the gate's egress admits every datastore and job in the estate, bounded only by each one's ingress |

## Reversibility

Expand Down
8 changes: 8 additions & 0 deletions docs/adr/model/0039-the-label-set-is-fixed.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ provider's render. It is on pod templates only, derived from an edge and
never authored, and never a workload's selector, so an edge added or removed
changes a template and nothing immutable.

**The member label is the same case, in the other direction.** Every gate
member's pods carry `gated.deploy-kit.jorisjonkers.dev/release-gate`, which the
Release Gate's NetworkPolicy reaches
([0035](0035-network-policy-is-default-deny-and-render-only.md)). Naming each
member in the gate's policy would put every gated Application into the delivery
Project's render. It is on pod templates only, derived from the release-gate
inputs, and never a selector.

## Alternatives
| option | cost if taken | why rejected |
|---|---|---|
Expand Down
6 changes: 6 additions & 0 deletions spec/v1/10-project-intent.md
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,12 @@ only, derived from the edges and never authored, and it is never a workload's
selector, so an edge added or removed changes the pod template and nothing
immutable.

A gate member's pods carry `gated.deploy-kit.jorisjonkers.dev/release-gate: "true"`,
which the Release Gate's policy admits
([chapter 16](16-dependencies.md#the-member-label)), on the same terms: on pod
templates only, derived from the release-gate inputs, never authored and never
a selector.

An estate-scoped Deliverable carries `managed-by` and nothing else: it belongs
to no Process and to no Application, and `part-of` on such an object would name a
Application that does not own it.
Expand Down
20 changes: 20 additions & 0 deletions spec/v1/16-dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,8 @@ producer.
| from the route tier carrying the audience | a route on the Application's `exposure` naming this Process | ingress |
| from the metrics stack, to the scrape port | the Process's `scrape` surface | ingress |
| from the Process's own backup identity, to the surface its backups dump | the surface the Platform document's `engines` names for the Process's `engine` | ingress |
| from the Release Gate, to a gate member's first surface | the Process being a member of its Application's release-gate inputs ([chapter 20](20-resolved-deployment.md#the-release-gate)), and the Platform document's `delivery.gate` naming the gate: every Process of the gate, on the port the member's Canary service targets, where the gate's own requests arrive ([chapter 55](55-delivery.md#the-release-gate)) | ingress |
| to every pod carrying the member label, on any port | every Process of the Application the Platform document's `delivery.gate` names ([The member label](#the-member-label)) | egress |
| from Flux's notification controller, to the Reporter's `http` surface | the Platform document's `delivery.reporter` naming the Application ([chapter 14](14-platform-intent.md#delivery-policy)): the first of its Processes that provides the surface, and no other. The peer is in the namespace `bootstrap.flux.sourceRef` names and is selected by the `app` label Flux's own install gives the controller, because Flux is in the bootstrap set and no Process, so nothing renders a label for it | ingress |
| to where the Kubernetes API answers, by address | the Process's `api`, and the Platform document's `apiAccess.server` ([Kubernetes API access is declared and admitted](#kubernetes-api-access-is-declared-and-admitted)) | egress |
| between a provider's Instances, on the surface its engine's move method syncs over | an open Move of the provider ([chapter 55](55-delivery.md#moves)): from the target Instance to the source while the sync runs, and back while reverse replication runs | egress and ingress |
Expand Down Expand Up @@ -576,6 +578,24 @@ starting and ending with a letter or digit. An Application whose id, joined by a
dot to a surface one of its Processes provides, is no label name is refused at
that Process as soon as the fragment is read: `E_SURFACE_LABEL_INVALID`.

### The member label

The Release Gate reaches every gate member without naming one. The pods of every
member, a Process its Application's release-gate inputs name, carry
`gated.deploy-kit.jorisjonkers.dev/release-gate: "true"`. The gate's policy
admits egress to every pod carrying it, in any namespace labelled
`app.kubernetes.io/managed-by: deploy-kit`, on any port, and each member's
policy admits the gate on the one port its Canary service targets, the port of
its first surface. The gate's rule reads no Project's Processes, and a member's
rule reads the Platform document's `delivery.gate` alone, so a member added,
changed or removed changes its own share and never the gate's
([0116](../../docs/adr/model/0116-a-project-owns-every-object-it-delivers.md)).

The gate's rule names no port because members serve on many and no one fact of
the delivery Project holds them; the member's own ingress is what bounds it. The
label is rendered only on a Process the release-gate inputs name, on pod
templates only, never in a selector, the same trust as the consumer label's.

### The baseline

Two rules are in the rendered set for every Process and appear in no
Expand Down
10 changes: 8 additions & 2 deletions spec/v1/20-resolved-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,16 @@ adapter reads the Resolved Deployment and nothing else
are spelled; its `surfaces`, each a name and a port, which a route, a scrape and
a policy name it by; and the peers its policy admits beyond its own edges: its
`ingress`, each peer tagged with the allow-set rule that admits it (`tier-proxy`,
`metrics-stack`, `backup` or `flux-events`, each naming a namespace and a
`metrics-stack`, `backup`, `flux-events` or `release-gate`, each naming a namespace and a
Process, or `consumer`, naming instead the surface whose
[consumer label](16-dependencies.md#the-consumer-label) it admits); the
surfaces it `consumes`, one per edge to an Application, as that label names
them; and its `egress`, the `cluster-dns` baseline and,
for a Process that holds a grant, the `secret-store`: the Process of the Secret
Store's Application that answers on its `http` surface
([chapter 16](16-dependencies.md#the-derived-allow-set)), and, for a Process that
([chapter 16](16-dependencies.md#the-derived-allow-set)), and, for a Process of
the Release Gate, `gate-members`, which names no peer: every pod carrying the
[member label](16-dependencies.md#the-member-label); and, for a Process that
declares it, its `internetEgress`, the reason it reaches the public internet
([chapter 16](16-dependencies.md#internet-egress)). An Application records
its `scrape`, the monitored surface at the Platform document's cadence; an
Expand Down Expand Up @@ -2047,6 +2049,9 @@ classDiagram
+string process
+int port
}
class GateMembersPeer {
+string rule
}
class InternetEgress {
+string reason
}
Expand Down Expand Up @@ -2285,6 +2290,7 @@ classDiagram
ResolvedProcess "1" *-- "0..*" ResolvedSurface : surfaces
ResolvedProcess "1" *-- "0..*" IngressPeer : ingress
ResolvedProcess "1" *-- "0..*" EgressPeer : egress
ResolvedProcess "1" *-- "0..1" GateMembersPeer : egress
ResolvedProcess "1" *-- "0..1" InternetEgress : internetEgress
ResolvedVolume "1" *-- "0..1" BackupPlan : backup
ResolvedProcess "1" *-- "0..1" ResolvedApiAccess : api
Expand Down
5 changes: 3 additions & 2 deletions spec/v1/55-delivery.md
Original file line number Diff line number Diff line change
Expand Up @@ -983,8 +983,9 @@ closes this, because the address is not in the name. A warmup therefore:
- never follows a redirect: a `3xx` is an answer, and it passes.

A check during analysis asks the member's Canary service, which the gate
names itself and no project file does. The gate reads everything else from
the Resolved Deployment.
names itself and no project file does, and the derived allow set admits that
request at both ends ([the member label](16-dependencies.md#the-member-label)).
The gate reads everything else from the Resolved Deployment.

**A member that did not change starts no analysis.** Flagger analyses a Canary
only when its target's pod template, or a configuration object it tracks,
Expand Down
2 changes: 1 addition & 1 deletion spec/v1/examples/auth/expected/resolved.json

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions spec/v1/examples/auth/rendered/apps/auth/auth/canary.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ spec:
metadata:
application: auth
process: auth-api
revision: sha256:a6d9f085b402c9364f8e0772c19dff934e115941d0d4002ec2293daed5d283df
revision: sha256:3f00477889937789250baec27dda1c3f513429fffa093075be1ed47324028142
paths: /api /api/v1/users/register
readiness: /api/actuator/health/readiness
hostnames: auth.jorisjonkers.dev
Expand All @@ -49,7 +49,7 @@ spec:
metadata:
application: auth
process: auth-api
revision: sha256:a6d9f085b402c9364f8e0772c19dff934e115941d0d4002ec2293daed5d283df
revision: sha256:3f00477889937789250baec27dda1c3f513429fffa093075be1ed47324028142
paths: /api /api/v1/users/register
readiness: /api/actuator/health/readiness
hostnames: auth.jorisjonkers.dev
Expand All @@ -61,7 +61,7 @@ spec:
metadata:
application: auth
process: auth-api
revision: sha256:a6d9f085b402c9364f8e0772c19dff934e115941d0d4002ec2293daed5d283df
revision: sha256:3f00477889937789250baec27dda1c3f513429fffa093075be1ed47324028142
paths: /api /api/v1/users/register
readiness: /api/actuator/health/readiness
hostnames: auth.jorisjonkers.dev
Expand Down Expand Up @@ -120,7 +120,7 @@ spec:
metadata:
application: auth
process: auth-ui
revision: sha256:a6d9f085b402c9364f8e0772c19dff934e115941d0d4002ec2293daed5d283df
revision: sha256:3f00477889937789250baec27dda1c3f513429fffa093075be1ed47324028142
paths: /
readiness: /
hostnames: auth.jorisjonkers.dev
Expand All @@ -132,7 +132,7 @@ spec:
metadata:
application: auth
process: auth-ui
revision: sha256:a6d9f085b402c9364f8e0772c19dff934e115941d0d4002ec2293daed5d283df
revision: sha256:3f00477889937789250baec27dda1c3f513429fffa093075be1ed47324028142
paths: /
readiness: /
hostnames: auth.jorisjonkers.dev
Expand All @@ -144,7 +144,7 @@ spec:
metadata:
application: auth
process: auth-ui
revision: sha256:a6d9f085b402c9364f8e0772c19dff934e115941d0d4002ec2293daed5d283df
revision: sha256:3f00477889937789250baec27dda1c3f513429fffa093075be1ed47324028142
paths: /
readiness: /
hostnames: auth.jorisjonkers.dev
Expand Down
6 changes: 3 additions & 3 deletions spec/v1/examples/auth/rendered/apps/auth/auth/migration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ metadata:
apiVersion: batch/v1
kind: Job
metadata:
name: auth-migration-a6d9f085b402
name: auth-migration-3f0047788993
namespace: auth-system
labels:
app.kubernetes.io/name: auth-migration
Expand Down Expand Up @@ -55,7 +55,7 @@ spec:
image: ghcr.io/jorisjonkers-dev/auth/auth-migration@sha256:5c7e9a1b3d5f7c9e1a3b5d7f9c1e3a5b7d9f1c3e5a7b9d1f3c5e7a9b1d3f5c7e
args:
- up
- a6d9f085b402
- 3f0047788993
env:
- name: DATABASE_HOST
value: postgres.data-system.svc.cluster.local
Expand Down Expand Up @@ -92,7 +92,7 @@ spec:
apiVersion: batch/v1
kind: Job
metadata:
name: auth-migration-down-a6d9f085b402
name: auth-migration-down-3f0047788993
namespace: auth-system
labels:
app.kubernetes.io/name: auth-migration
Expand Down
20 changes: 20 additions & 0 deletions spec/v1/examples/auth/rendered/apps/auth/auth/networkpolicy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ spec:
ports:
- protocol: TCP
port: 8080
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: delivery-system
podSelector:
matchLabels:
app.kubernetes.io/instance: release-gate
ports:
- protocol: TCP
port: 8080
egress:
- to:
- namespaceSelector:
Expand Down Expand Up @@ -151,6 +161,16 @@ spec:
ports:
- protocol: TCP
port: 80
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: delivery-system
podSelector:
matchLabels:
app.kubernetes.io/instance: release-gate
ports:
- protocol: TCP
port: 80
egress:
- to:
- namespaceSelector:
Expand Down
2 changes: 2 additions & 0 deletions spec/v1/examples/auth/rendered/apps/auth/auth/workload.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ spec:
consumes.deploy-kit.jorisjonkers.dev/platform-valkey.redis: "true"
consumes.deploy-kit.jorisjonkers.dev/platform-rabbitmq.amqp: "true"
consumes.deploy-kit.jorisjonkers.dev/stalwart.smtp: "true"
gated.deploy-kit.jorisjonkers.dev/release-gate: "true"
annotations:
image.jorisjonkers.dev/auth-api: 2.3.0
spec:
Expand Down Expand Up @@ -177,6 +178,7 @@ spec:
app.kubernetes.io/part-of: auth
app.kubernetes.io/managed-by: deploy-kit
app.kubernetes.io/component: static
gated.deploy-kit.jorisjonkers.dev/release-gate: "true"
annotations:
image.jorisjonkers.dev/auth-ui: 2.3.0
spec:
Expand Down
Loading
Loading