diff --git a/CONTEXT.md b/CONTEXT.md index cef0efbc2..6ec7681de 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -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 diff --git a/docs/adr/model/0035-network-policy-is-default-deny-and-render-only.md b/docs/adr/model/0035-network-policy-is-default-deny-and-render-only.md index b17d7dc6c..9a344ffa8 100644 --- a/docs/adr/model/0035-network-policy-is-default-deny-and-render-only.md +++ b/docs/adr/model/0035-network-policy-is-default-deny-and-render-only.md @@ -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. @@ -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 diff --git a/docs/adr/model/0039-the-label-set-is-fixed.md b/docs/adr/model/0039-the-label-set-is-fixed.md index e421b4995..6d56e7f61 100644 --- a/docs/adr/model/0039-the-label-set-is-fixed.md +++ b/docs/adr/model/0039-the-label-set-is-fixed.md @@ -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 | |---|---|---| diff --git a/spec/v1/10-project-intent.md b/spec/v1/10-project-intent.md index 319f9a120..a1cbd0445 100644 --- a/spec/v1/10-project-intent.md +++ b/spec/v1/10-project-intent.md @@ -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. diff --git a/spec/v1/16-dependencies.md b/spec/v1/16-dependencies.md index 7fa2034aa..e03a7acb8 100644 --- a/spec/v1/16-dependencies.md +++ b/spec/v1/16-dependencies.md @@ -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 | @@ -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 diff --git a/spec/v1/20-resolved-deployment.md b/spec/v1/20-resolved-deployment.md index 6ace6e91a..937e3f635 100644 --- a/spec/v1/20-resolved-deployment.md +++ b/spec/v1/20-resolved-deployment.md @@ -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 @@ -2047,6 +2049,9 @@ classDiagram +string process +int port } + class GateMembersPeer { + +string rule + } class InternetEgress { +string reason } @@ -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 diff --git a/spec/v1/55-delivery.md b/spec/v1/55-delivery.md index e47a9e49b..f39e0b3cd 100644 --- a/spec/v1/55-delivery.md +++ b/spec/v1/55-delivery.md @@ -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, diff --git a/spec/v1/examples/auth/expected/resolved.json b/spec/v1/examples/auth/expected/resolved.json index e3e37efe0..c3c65bda5 100644 --- a/spec/v1/examples/auth/expected/resolved.json +++ b/spec/v1/examples/auth/expected/resolved.json @@ -1 +1 @@ -{"alertClass":"page","apiVersion":"resolved.jorisjonkers.dev/v1","exposure":[{"certificates":"acme","dns":{"proxied":true,"target":"ingress.jorisjonkers.dev"},"host":"auth.jorisjonkers.dev","ingressClass":"traefik-public","listener":"tls","name":"public","proxy":{"application":"traefik-public","namespace":"edge-system"},"routes":[{"audience":"anonymous","match":"exact","middleware":[{"contentPolicy":"strict","kind":"security-headers"},{"allowance":10,"burst":10,"clientAddress":"cloudflare","kind":"rate-limit","per":"1h"}],"path":"/api/v1/users/register","precedence":1,"process":"auth-api","surface":"http"},{"audience":"anonymous","match":"prefix","middleware":[{"contentPolicy":"strict","kind":"security-headers"}],"path":"/api","precedence":2,"process":"auth-api","surface":"http"},{"audience":"anonymous","match":"prefix","middleware":[{"contentPolicy":"strict","kind":"security-headers"}],"path":"/","precedence":3,"process":"auth-ui","surface":"http"}],"tier":"public-frankfurt"}],"id":"auth","kind":"ResolvedApplication","migration":{"consumes":["platform-postgres.postgres"],"cpu":"100m","credential":{"delivery":"self","engine":"database","paths":[{"allows":["read"],"path":"database/creds/auth-owner"}]},"database":{"host":"postgres.data-system.svc.cluster.local","name":"auth_db","port":5432},"deadline":"10m","egress":[{"namespace":"data-system","port":5432,"process":"postgres","rule":"datastore"},{"namespace":"secrets-system","port":8200,"process":"vault","rule":"secret-store"},{"namespace":"kube-system","port":53,"rule":"cluster-dns"}],"gid":1000,"identity":"auth-migration","memory":"256Mi","nonTransactional":false,"runner":"ghcr.io/jorisjonkers-dev/auth/auth-migration@sha256:5c7e9a1b3d5f7c9e1a3b5d7f9c1e3a5b7d9f1c3e5a7b9d1f3c5e7a9b1d3f5c7e","runnerVersion":"2.3.0","scratch":"64Mi","testedAgainst":"sha256:2f4a6c8e0b2d4f6a8c0e2b4d6f8a0c2e4b6d8f0a2c4e6b8d0f2a4c6e8b0d2f4a","uid":1000},"namespace":"auth-system","processes":[{"consumes":["platform-postgres.postgres","platform-valkey.redis","platform-rabbitmq.amqp","stalwart.smtp"],"cpu":"250m","cutover":"continuous","deadline":"1920s","dependencies":[{"address":"platform-postgres:5432","application":"platform-postgres","peers":[{"namespace":"data-system","port":5432,"process":"postgres"}],"surface":"postgres","target":"postgres.data-system.svc.cluster.local"},{"address":"platform-valkey:6379","application":"platform-valkey","peers":[{"namespace":"data-system","port":6379,"process":"valkey"}],"surface":"redis","target":"valkey.data-system.svc.cluster.local"},{"address":"platform-rabbitmq:5672","application":"platform-rabbitmq","peers":[{"namespace":"data-system","port":5672,"process":"rabbitmq"}],"surface":"amqp","target":"rabbitmq.data-system.svc.cluster.local"},{"address":"stalwart:25","application":"stalwart","peers":[{"namespace":"mail-system","port":25,"process":"stalwart"}],"surface":"smtp","target":"stalwart.mail-system.svc.cluster.local"}],"egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"},{"namespace":"secrets-system","port":8200,"process":"vault","rule":"secret-store"}],"environment":[{"name":"AUTH_ISSUER","value":"https://auth.jorisjonkers.dev"},{"name":"AUTH_LOGIN_URL","value":"https://auth.jorisjonkers.dev/login"},{"name":"CONFIRMATION_URL","value":"https://auth.jorisjonkers.dev/confirm"},{"name":"DB_HOST","value":"platform-postgres"},{"name":"DB_PORT","value":"5432"},{"name":"DEPLOYMENT_ENVIRONMENT","value":"production"},{"name":"MAIL_HOST","value":"stalwart"},{"name":"MAIL_PORT","value":"25"},{"name":"OTEL_EXPORTER_OTLP_ENDPOINT","value":"http://otel-collector.observability-system.svc.cluster.local:4317"},{"name":"OTEL_SERVICE_NAME","value":"auth-api"},{"name":"PORT","value":"8080"},{"name":"SESSION_COOKIE_SECURE","value":"true"},{"name":"SPRING_CONFIG_IMPORT","value":"vault://"},{"name":"SPRING_PROFILES_ACTIVE","value":"prod"},{"name":"SPRING_RABBITMQ_HOST","value":"platform-rabbitmq"},{"name":"SPRING_RABBITMQ_PORT","value":"5672"},{"name":"VALKEY_HOST","value":"platform-valkey"},{"name":"VALKEY_PORT","value":"6379"},{"name":"VAULT_AUTHENTICATION","value":"KUBERNETES"},{"name":"VAULT_ENABLED","value":"true"},{"name":"VAULT_KUBERNETES_ROLE","value":"auth-system-auth-api"}],"gid":1000,"hardening":"restricted","identity":"auth-api","identityToken":true,"image":"ghcr.io/jorisjonkers-dev/auth/auth-api@sha256:3e5a7c9b1d3f5e7a9c1b3d5f7e9a1c3b5d7f9e1a3c5b7d9f1e3a5c7b9d1f3e5a","imageVersion":"2.3.0","ingress":[{"namespace":"edge-system","port":8080,"process":"traefik-public","rule":"tier-proxy"},{"namespace":"observability-system","port":8080,"process":"prometheus","rule":"metrics-stack"},{"consumes":"auth.http","port":8080,"rule":"consumer"}],"lifecycle":"application","liveness":{"failures":3,"path":"/api/actuator/health/liveness","period":"10s","port":8081,"timeout":"5s"},"memory":"768Mi","name":"auth-api","placement":{"eligibleNodes":["frankfurt-contabo-1"]},"priorityClass":"platform-critical-app","readiness":{"failures":3,"path":"/api/actuator/health/readiness","period":"10s","port":8081,"timeout":"5s"},"replicas":2,"runtime":"jvm","secrets":[{"access":"read","delivery":"self","keys":["user","password"],"path":"secret/data/platform/postgres/auth"},{"access":"read","delivery":"self","keys":["mail.password","valkey.password","rabbitmq.user","rabbitmq.password"],"path":"secret/data/auth-api"},{"delivery":"self","engine":"transit","paths":[{"allows":["update"],"path":"transit/sign/auth-api-jwt"},{"allows":["update"],"path":"transit/keys/auth-api-jwt/rotate"},{"allows":["read"],"path":"transit/keys/auth-api-jwt"}]},{"delivery":"self","engine":"database","paths":[{"allows":["read"],"path":"database/creds/auth-data"}]}],"startup":{"failures":120,"path":"/api/actuator/health/liveness","period":"5s","port":8081,"timeout":"5s"},"surfaces":[{"name":"http","port":8080}],"switchover":"blue-green","uid":1000,"writablePaths":[{"path":"/tmp","size":"64Mi"}]},{"cpu":"10m","cutover":"continuous","deadline":"210s","egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"}],"gid":101,"hardening":"restricted","identity":"auth-ui","identityToken":false,"image":"ghcr.io/jorisjonkers-dev/auth/auth-ui@sha256:8b0d2f4a6c8e0b2d4f6a8c0e2b4d6f8a0c2e4b6d8f0a2c4e6b8d0f2a4c6e8b0d","imageVersion":"2.3.0","ingress":[{"namespace":"edge-system","port":80,"process":"traefik-public","rule":"tier-proxy"},{"consumes":"auth.http","port":80,"rule":"consumer"}],"lifecycle":"application","liveness":{"failures":3,"path":"/","period":"10s","port":80,"timeout":"5s"},"memory":"64Mi","name":"auth-ui","placement":{"eligibleNodes":["enschede-t1000-1","enschede-rx7900xtx-1","enschede-gtx-960m-1","enschede-pi-1","enschede-pi-2","enschede-pi-3","frankfurt-contabo-1"]},"priorityClass":"platform-critical-app","readiness":{"failures":3,"path":"/","period":"10s","port":80,"timeout":"5s"},"replicas":1,"runtime":"static","startup":{"failures":6,"path":"/","period":"5s","port":80,"timeout":"5s"},"surfaces":[{"name":"http","port":80}],"switchover":"blue-green","uid":101,"writablePaths":[{"path":"/var/cache/nginx","size":"64Mi"},{"path":"/var/run","size":"64Mi"}]}],"project":"auth","provenance":{"inputDigests":[{"digest":"sha256:a8ffae4a90542ed792d9e285333f706f9964bd0d3888e6e9ca686932f0c44a5c","input":"intent-fragment","name":"auth"},{"digest":"sha256:5391c90578e87c9e5a3697dd7f32268e92aa578786ba33a2f82ebe50b392a42e","input":"intent-fragment","name":"data"},{"digest":"sha256:ebb5134deeb55f320e28609054a0aca85a359f3869501a3a8b7560d7d05a4277","input":"intent-fragment","name":"delivery"},{"digest":"sha256:9a29fae42c0c6cd4f9824d7af3d9ecc68c068a3a00d1f9488bfbb70c882ea565","input":"intent-fragment","name":"edge"},{"digest":"sha256:c45963dcb3911475bff7d4fd17c0ae570230bdadba4ff2a68c75272d9219e762","input":"intent-fragment","name":"identity"},{"digest":"sha256:447e1500b09b608cecc0f93ebb598a60e655690822ecc63395e4e40eaadb98cb","input":"intent-fragment","name":"mail"},{"digest":"sha256:5916815b3b70a0de96137a6614f9356003985016bbf078bafe098433c9aab7d1","input":"intent-fragment","name":"observability"},{"digest":"sha256:31309996835674cc4f037f8e19897ddbe4a7fa8a487a3efc613e251caeed0853","input":"intent-fragment","name":"secrets"},{"digest":"sha256:f7e7e1e0a33d9528e11d956c36b5a6b8f37f758fb98aecdd74ea3eaf7286d60e","input":"platform-intent","name":"jorisjonkers.dev"},{"digest":"sha256:a989e80337efb294fa1a7175b3ad3eea8bed9bf13fbd503a26691c9265575fc2","input":"node-contract","name":"production"},{"digest":"sha256:f09f02097230414af40101e8ebe053e62d679c085a7d1fed9d860c8ba151b68b","input":"images-lock","name":"estate"},{"digest":"sha256:202e5e2a22ab20cfe5b6e85cbd8139cb13ed3b199b2f1af4380068d21d54ff1f","input":"cluster-state","name":"production"}],"renderHash":"sha256:6489f4c28e098191c4e7e8a90d231de858822f965ee3ea22b02372b5c9dd0f0b","schemaPackageIntegrity":"sha256:5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b"},"reconcileAfter":["apps-data","apps-edge","apps-mail","apps-secrets"],"reconcileUnit":"apps-auth","releaseGate":{"analysis":{"errorRate":"1%","interval":"30s","iterations":4,"latency":"500ms","threshold":3},"deadline":"1920s","endpoint":"http://release-gate.delivery-system.svc.cluster.local:8080","members":[{"checks":["error-rate","latency"],"process":"auth-api","readiness":{"path":"/api/actuator/health/readiness","port":8081}},{"process":"auth-ui","readiness":{"path":"/","port":80}}],"migration":{"identity":"auth-migration","nonTransactional":false,"testedAgainst":"sha256:2f4a6c8e0b2d4f6a8c0e2b4d6f8a0c2e4b6d8f0a2c4e6b8d0f2a4c6e8b0d2f4a"}},"revision":"sha256:a6d9f085b402c9364f8e0772c19dff934e115941d0d4002ec2293daed5d283df","scrape":{"interval":"30s","labels":{"release":"metrics-stack"},"path":"/api/actuator/prometheus","process":"auth-api","surface":"http","timeout":"10s"},"secretStore":"http://vault.secrets-system.svc.cluster.local:8200"} \ No newline at end of file +{"alertClass":"page","apiVersion":"resolved.jorisjonkers.dev/v1","exposure":[{"certificates":"acme","dns":{"proxied":true,"target":"ingress.jorisjonkers.dev"},"host":"auth.jorisjonkers.dev","ingressClass":"traefik-public","listener":"tls","name":"public","proxy":{"application":"traefik-public","namespace":"edge-system"},"routes":[{"audience":"anonymous","match":"exact","middleware":[{"contentPolicy":"strict","kind":"security-headers"},{"allowance":10,"burst":10,"clientAddress":"cloudflare","kind":"rate-limit","per":"1h"}],"path":"/api/v1/users/register","precedence":1,"process":"auth-api","surface":"http"},{"audience":"anonymous","match":"prefix","middleware":[{"contentPolicy":"strict","kind":"security-headers"}],"path":"/api","precedence":2,"process":"auth-api","surface":"http"},{"audience":"anonymous","match":"prefix","middleware":[{"contentPolicy":"strict","kind":"security-headers"}],"path":"/","precedence":3,"process":"auth-ui","surface":"http"}],"tier":"public-frankfurt"}],"id":"auth","kind":"ResolvedApplication","migration":{"consumes":["platform-postgres.postgres"],"cpu":"100m","credential":{"delivery":"self","engine":"database","paths":[{"allows":["read"],"path":"database/creds/auth-owner"}]},"database":{"host":"postgres.data-system.svc.cluster.local","name":"auth_db","port":5432},"deadline":"10m","egress":[{"namespace":"data-system","port":5432,"process":"postgres","rule":"datastore"},{"namespace":"secrets-system","port":8200,"process":"vault","rule":"secret-store"},{"namespace":"kube-system","port":53,"rule":"cluster-dns"}],"gid":1000,"identity":"auth-migration","memory":"256Mi","nonTransactional":false,"runner":"ghcr.io/jorisjonkers-dev/auth/auth-migration@sha256:5c7e9a1b3d5f7c9e1a3b5d7f9c1e3a5b7d9f1c3e5a7b9d1f3c5e7a9b1d3f5c7e","runnerVersion":"2.3.0","scratch":"64Mi","testedAgainst":"sha256:2f4a6c8e0b2d4f6a8c0e2b4d6f8a0c2e4b6d8f0a2c4e6b8d0f2a4c6e8b0d2f4a","uid":1000},"namespace":"auth-system","processes":[{"consumes":["platform-postgres.postgres","platform-valkey.redis","platform-rabbitmq.amqp","stalwart.smtp"],"cpu":"250m","cutover":"continuous","deadline":"1920s","dependencies":[{"address":"platform-postgres:5432","application":"platform-postgres","peers":[{"namespace":"data-system","port":5432,"process":"postgres"}],"surface":"postgres","target":"postgres.data-system.svc.cluster.local"},{"address":"platform-valkey:6379","application":"platform-valkey","peers":[{"namespace":"data-system","port":6379,"process":"valkey"}],"surface":"redis","target":"valkey.data-system.svc.cluster.local"},{"address":"platform-rabbitmq:5672","application":"platform-rabbitmq","peers":[{"namespace":"data-system","port":5672,"process":"rabbitmq"}],"surface":"amqp","target":"rabbitmq.data-system.svc.cluster.local"},{"address":"stalwart:25","application":"stalwart","peers":[{"namespace":"mail-system","port":25,"process":"stalwart"}],"surface":"smtp","target":"stalwart.mail-system.svc.cluster.local"}],"egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"},{"namespace":"secrets-system","port":8200,"process":"vault","rule":"secret-store"}],"environment":[{"name":"AUTH_ISSUER","value":"https://auth.jorisjonkers.dev"},{"name":"AUTH_LOGIN_URL","value":"https://auth.jorisjonkers.dev/login"},{"name":"CONFIRMATION_URL","value":"https://auth.jorisjonkers.dev/confirm"},{"name":"DB_HOST","value":"platform-postgres"},{"name":"DB_PORT","value":"5432"},{"name":"DEPLOYMENT_ENVIRONMENT","value":"production"},{"name":"MAIL_HOST","value":"stalwart"},{"name":"MAIL_PORT","value":"25"},{"name":"OTEL_EXPORTER_OTLP_ENDPOINT","value":"http://otel-collector.observability-system.svc.cluster.local:4317"},{"name":"OTEL_SERVICE_NAME","value":"auth-api"},{"name":"PORT","value":"8080"},{"name":"SESSION_COOKIE_SECURE","value":"true"},{"name":"SPRING_CONFIG_IMPORT","value":"vault://"},{"name":"SPRING_PROFILES_ACTIVE","value":"prod"},{"name":"SPRING_RABBITMQ_HOST","value":"platform-rabbitmq"},{"name":"SPRING_RABBITMQ_PORT","value":"5672"},{"name":"VALKEY_HOST","value":"platform-valkey"},{"name":"VALKEY_PORT","value":"6379"},{"name":"VAULT_AUTHENTICATION","value":"KUBERNETES"},{"name":"VAULT_ENABLED","value":"true"},{"name":"VAULT_KUBERNETES_ROLE","value":"auth-system-auth-api"}],"gid":1000,"hardening":"restricted","identity":"auth-api","identityToken":true,"image":"ghcr.io/jorisjonkers-dev/auth/auth-api@sha256:3e5a7c9b1d3f5e7a9c1b3d5f7e9a1c3b5d7f9e1a3c5b7d9f1e3a5c7b9d1f3e5a","imageVersion":"2.3.0","ingress":[{"namespace":"edge-system","port":8080,"process":"traefik-public","rule":"tier-proxy"},{"namespace":"observability-system","port":8080,"process":"prometheus","rule":"metrics-stack"},{"consumes":"auth.http","port":8080,"rule":"consumer"},{"namespace":"delivery-system","port":8080,"process":"release-gate","rule":"release-gate"}],"lifecycle":"application","liveness":{"failures":3,"path":"/api/actuator/health/liveness","period":"10s","port":8081,"timeout":"5s"},"memory":"768Mi","name":"auth-api","placement":{"eligibleNodes":["frankfurt-contabo-1"]},"priorityClass":"platform-critical-app","readiness":{"failures":3,"path":"/api/actuator/health/readiness","period":"10s","port":8081,"timeout":"5s"},"replicas":2,"runtime":"jvm","secrets":[{"access":"read","delivery":"self","keys":["user","password"],"path":"secret/data/platform/postgres/auth"},{"access":"read","delivery":"self","keys":["mail.password","valkey.password","rabbitmq.user","rabbitmq.password"],"path":"secret/data/auth-api"},{"delivery":"self","engine":"transit","paths":[{"allows":["update"],"path":"transit/sign/auth-api-jwt"},{"allows":["update"],"path":"transit/keys/auth-api-jwt/rotate"},{"allows":["read"],"path":"transit/keys/auth-api-jwt"}]},{"delivery":"self","engine":"database","paths":[{"allows":["read"],"path":"database/creds/auth-data"}]}],"startup":{"failures":120,"path":"/api/actuator/health/liveness","period":"5s","port":8081,"timeout":"5s"},"surfaces":[{"name":"http","port":8080}],"switchover":"blue-green","uid":1000,"writablePaths":[{"path":"/tmp","size":"64Mi"}]},{"cpu":"10m","cutover":"continuous","deadline":"210s","egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"}],"gid":101,"hardening":"restricted","identity":"auth-ui","identityToken":false,"image":"ghcr.io/jorisjonkers-dev/auth/auth-ui@sha256:8b0d2f4a6c8e0b2d4f6a8c0e2b4d6f8a0c2e4b6d8f0a2c4e6b8d0f2a4c6e8b0d","imageVersion":"2.3.0","ingress":[{"namespace":"edge-system","port":80,"process":"traefik-public","rule":"tier-proxy"},{"consumes":"auth.http","port":80,"rule":"consumer"},{"namespace":"delivery-system","port":80,"process":"release-gate","rule":"release-gate"}],"lifecycle":"application","liveness":{"failures":3,"path":"/","period":"10s","port":80,"timeout":"5s"},"memory":"64Mi","name":"auth-ui","placement":{"eligibleNodes":["enschede-t1000-1","enschede-rx7900xtx-1","enschede-gtx-960m-1","enschede-pi-1","enschede-pi-2","enschede-pi-3","frankfurt-contabo-1"]},"priorityClass":"platform-critical-app","readiness":{"failures":3,"path":"/","period":"10s","port":80,"timeout":"5s"},"replicas":1,"runtime":"static","startup":{"failures":6,"path":"/","period":"5s","port":80,"timeout":"5s"},"surfaces":[{"name":"http","port":80}],"switchover":"blue-green","uid":101,"writablePaths":[{"path":"/var/cache/nginx","size":"64Mi"},{"path":"/var/run","size":"64Mi"}]}],"project":"auth","provenance":{"inputDigests":[{"digest":"sha256:a8ffae4a90542ed792d9e285333f706f9964bd0d3888e6e9ca686932f0c44a5c","input":"intent-fragment","name":"auth"},{"digest":"sha256:5391c90578e87c9e5a3697dd7f32268e92aa578786ba33a2f82ebe50b392a42e","input":"intent-fragment","name":"data"},{"digest":"sha256:ebb5134deeb55f320e28609054a0aca85a359f3869501a3a8b7560d7d05a4277","input":"intent-fragment","name":"delivery"},{"digest":"sha256:9a29fae42c0c6cd4f9824d7af3d9ecc68c068a3a00d1f9488bfbb70c882ea565","input":"intent-fragment","name":"edge"},{"digest":"sha256:c45963dcb3911475bff7d4fd17c0ae570230bdadba4ff2a68c75272d9219e762","input":"intent-fragment","name":"identity"},{"digest":"sha256:447e1500b09b608cecc0f93ebb598a60e655690822ecc63395e4e40eaadb98cb","input":"intent-fragment","name":"mail"},{"digest":"sha256:5916815b3b70a0de96137a6614f9356003985016bbf078bafe098433c9aab7d1","input":"intent-fragment","name":"observability"},{"digest":"sha256:31309996835674cc4f037f8e19897ddbe4a7fa8a487a3efc613e251caeed0853","input":"intent-fragment","name":"secrets"},{"digest":"sha256:f7e7e1e0a33d9528e11d956c36b5a6b8f37f758fb98aecdd74ea3eaf7286d60e","input":"platform-intent","name":"jorisjonkers.dev"},{"digest":"sha256:a989e80337efb294fa1a7175b3ad3eea8bed9bf13fbd503a26691c9265575fc2","input":"node-contract","name":"production"},{"digest":"sha256:f09f02097230414af40101e8ebe053e62d679c085a7d1fed9d860c8ba151b68b","input":"images-lock","name":"estate"},{"digest":"sha256:202e5e2a22ab20cfe5b6e85cbd8139cb13ed3b199b2f1af4380068d21d54ff1f","input":"cluster-state","name":"production"}],"renderHash":"sha256:6489f4c28e098191c4e7e8a90d231de858822f965ee3ea22b02372b5c9dd0f0b","schemaPackageIntegrity":"sha256:5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b"},"reconcileAfter":["apps-data","apps-edge","apps-mail","apps-secrets"],"reconcileUnit":"apps-auth","releaseGate":{"analysis":{"errorRate":"1%","interval":"30s","iterations":4,"latency":"500ms","threshold":3},"deadline":"1920s","endpoint":"http://release-gate.delivery-system.svc.cluster.local:8080","members":[{"checks":["error-rate","latency"],"process":"auth-api","readiness":{"path":"/api/actuator/health/readiness","port":8081}},{"process":"auth-ui","readiness":{"path":"/","port":80}}],"migration":{"identity":"auth-migration","nonTransactional":false,"testedAgainst":"sha256:2f4a6c8e0b2d4f6a8c0e2b4d6f8a0c2e4b6d8f0a2c4e6b8d0f2a4c6e8b0d2f4a"}},"revision":"sha256:3f00477889937789250baec27dda1c3f513429fffa093075be1ed47324028142","scrape":{"interval":"30s","labels":{"release":"metrics-stack"},"path":"/api/actuator/prometheus","process":"auth-api","surface":"http","timeout":"10s"},"secretStore":"http://vault.secrets-system.svc.cluster.local:8200"} \ No newline at end of file diff --git a/spec/v1/examples/auth/rendered/apps/auth/auth/canary.yaml b/spec/v1/examples/auth/rendered/apps/auth/auth/canary.yaml index 34bf88590..2bef2da4d 100644 --- a/spec/v1/examples/auth/rendered/apps/auth/auth/canary.yaml +++ b/spec/v1/examples/auth/rendered/apps/auth/auth/canary.yaml @@ -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 @@ -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 @@ -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 @@ -120,7 +120,7 @@ spec: metadata: application: auth process: auth-ui - revision: sha256:a6d9f085b402c9364f8e0772c19dff934e115941d0d4002ec2293daed5d283df + revision: sha256:3f00477889937789250baec27dda1c3f513429fffa093075be1ed47324028142 paths: / readiness: / hostnames: auth.jorisjonkers.dev @@ -132,7 +132,7 @@ spec: metadata: application: auth process: auth-ui - revision: sha256:a6d9f085b402c9364f8e0772c19dff934e115941d0d4002ec2293daed5d283df + revision: sha256:3f00477889937789250baec27dda1c3f513429fffa093075be1ed47324028142 paths: / readiness: / hostnames: auth.jorisjonkers.dev @@ -144,7 +144,7 @@ spec: metadata: application: auth process: auth-ui - revision: sha256:a6d9f085b402c9364f8e0772c19dff934e115941d0d4002ec2293daed5d283df + revision: sha256:3f00477889937789250baec27dda1c3f513429fffa093075be1ed47324028142 paths: / readiness: / hostnames: auth.jorisjonkers.dev diff --git a/spec/v1/examples/auth/rendered/apps/auth/auth/migration.yaml b/spec/v1/examples/auth/rendered/apps/auth/auth/migration.yaml index a86b4be93..1f37063ca 100644 --- a/spec/v1/examples/auth/rendered/apps/auth/auth/migration.yaml +++ b/spec/v1/examples/auth/rendered/apps/auth/auth/migration.yaml @@ -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 @@ -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 @@ -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 diff --git a/spec/v1/examples/auth/rendered/apps/auth/auth/networkpolicy.yaml b/spec/v1/examples/auth/rendered/apps/auth/auth/networkpolicy.yaml index 3c6e2bef7..69605586c 100644 --- a/spec/v1/examples/auth/rendered/apps/auth/auth/networkpolicy.yaml +++ b/spec/v1/examples/auth/rendered/apps/auth/auth/networkpolicy.yaml @@ -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: @@ -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: diff --git a/spec/v1/examples/auth/rendered/apps/auth/auth/workload.yaml b/spec/v1/examples/auth/rendered/apps/auth/auth/workload.yaml index 914673f69..fbe686775 100644 --- a/spec/v1/examples/auth/rendered/apps/auth/auth/workload.yaml +++ b/spec/v1/examples/auth/rendered/apps/auth/auth/workload.yaml @@ -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: @@ -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: diff --git a/spec/v1/examples/delivery/expected/resolved.json b/spec/v1/examples/delivery/expected/resolved.json index 6b8b99c29..fc9eee272 100644 --- a/spec/v1/examples/delivery/expected/resolved.json +++ b/spec/v1/examples/delivery/expected/resolved.json @@ -1 +1 @@ -{"apiVersion":"resolved.jorisjonkers.dev/v1","id":"release-gate","kind":"ResolvedApplication","namespace":"delivery-system","processes":[{"api":{"reason":"the Release Gate answers Flagger from what the cluster holds, so it reads each gated Application's inputs, Canaries, Deployments, Jobs and pods; it finds the gated Applications by listing ConfigMaps, keeps each one's release record in a ConfigMap of its own, unsuspends the migration Jobs it starts, and deletes a gated Application's Vault sync objects of an older revision once it serves the new one","rules":[{"group":"core","objects":["configmaps"],"verbs":["get","list","create","update"]},{"group":"core","objects":["pods"],"verbs":["list"]},{"group":"apps","objects":["deployments"],"verbs":["get"]},{"group":"batch","objects":["jobs"],"verbs":["list","patch"]},{"group":"flagger.app","objects":["canaries"],"verbs":["get"]},{"group":"secrets.hashicorp.com","objects":["vaultauths","vaultstaticsecrets","vaultdynamicsecrets"],"verbs":["list","delete"]}],"server":[{"cidr":"10.43.0.1/32","port":443}]},"cpu":"25m","cutover":"continuous","deadline":"150s","egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"}],"gid":1000,"hardening":"restricted","identity":"release-gate","identityToken":true,"image":"ghcr.io/jorisjonkers-dev/delivery/release-gate@sha256:3e7a9c1d5b2f8e4a6c0d9b7e5f3a1c8d6e4b2a0f9c7e5d3b1a8f6e4c2d0b9a7e","imageVersion":"1.0.0","ingress":[{"consumes":"release-gate.http","port":8080,"rule":"consumer"}],"lifecycle":"application","liveness":{"failures":3,"path":"/healthz","period":"10s","port":8080,"timeout":"5s"},"memory":"64Mi","name":"release-gate","placement":{"eligibleNodes":["enschede-t1000-1","enschede-rx7900xtx-1","enschede-gtx-960m-1","enschede-pi-1","enschede-pi-2","enschede-pi-3","frankfurt-contabo-1"]},"priorityClass":"platform-critical-app","readiness":{"failures":3,"path":"/readyz","period":"10s","port":8080,"timeout":"5s"},"replicas":1,"runtime":"none","startup":{"failures":2,"path":"/healthz","period":"5s","port":8080,"timeout":"5s"},"surfaces":[{"name":"http","port":8080}],"switchover":"rolling","uid":1000}],"project":"delivery","provenance":{"inputDigests":[{"digest":"sha256:ebb5134deeb55f320e28609054a0aca85a359f3869501a3a8b7560d7d05a4277","input":"intent-fragment","name":"delivery"},{"digest":"sha256:9a29fae42c0c6cd4f9824d7af3d9ecc68c068a3a00d1f9488bfbb70c882ea565","input":"intent-fragment","name":"edge"},{"digest":"sha256:c45963dcb3911475bff7d4fd17c0ae570230bdadba4ff2a68c75272d9219e762","input":"intent-fragment","name":"identity"},{"digest":"sha256:447e1500b09b608cecc0f93ebb598a60e655690822ecc63395e4e40eaadb98cb","input":"intent-fragment","name":"mail"},{"digest":"sha256:35d5e3643093422537ea72def0f4b19e93ed319a2d0192fc7846342970ce8797","input":"intent-fragment","name":"notes"},{"digest":"sha256:5916815b3b70a0de96137a6614f9356003985016bbf078bafe098433c9aab7d1","input":"intent-fragment","name":"observability"},{"digest":"sha256:31309996835674cc4f037f8e19897ddbe4a7fa8a487a3efc613e251caeed0853","input":"intent-fragment","name":"secrets"},{"digest":"sha256:f7e7e1e0a33d9528e11d956c36b5a6b8f37f758fb98aecdd74ea3eaf7286d60e","input":"platform-intent","name":"jorisjonkers.dev"},{"digest":"sha256:a989e80337efb294fa1a7175b3ad3eea8bed9bf13fbd503a26691c9265575fc2","input":"node-contract","name":"production"},{"digest":"sha256:f09f02097230414af40101e8ebe053e62d679c085a7d1fed9d860c8ba151b68b","input":"images-lock","name":"estate"},{"digest":"sha256:202e5e2a22ab20cfe5b6e85cbd8139cb13ed3b199b2f1af4380068d21d54ff1f","input":"cluster-state","name":"production"}],"renderHash":"sha256:c944fc023ec1bda7d6df2150acd36374ae0d7f8aae46e54baf8c2ec48f1e2f1a","schemaPackageIntegrity":"sha256:5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b"},"reconcileUnit":"apps-delivery","revision":"sha256:8c1ae22588fd8dc3a89267dbaadce6d21196acf582c78062a2a2c8cbed923252"} \ No newline at end of file +{"apiVersion":"resolved.jorisjonkers.dev/v1","id":"release-gate","kind":"ResolvedApplication","namespace":"delivery-system","processes":[{"api":{"reason":"the Release Gate answers Flagger from what the cluster holds, so it reads each gated Application's inputs, Canaries, Deployments, Jobs and pods; it finds the gated Applications by listing ConfigMaps, keeps each one's release record in a ConfigMap of its own, unsuspends the migration Jobs it starts, and deletes a gated Application's Vault sync objects of an older revision once it serves the new one","rules":[{"group":"core","objects":["configmaps"],"verbs":["get","list","create","update"]},{"group":"core","objects":["pods"],"verbs":["list"]},{"group":"apps","objects":["deployments"],"verbs":["get"]},{"group":"batch","objects":["jobs"],"verbs":["list","patch"]},{"group":"flagger.app","objects":["canaries"],"verbs":["get"]},{"group":"secrets.hashicorp.com","objects":["vaultauths","vaultstaticsecrets","vaultdynamicsecrets"],"verbs":["list","delete"]}],"server":[{"cidr":"10.43.0.1/32","port":443}]},"cpu":"25m","cutover":"continuous","deadline":"150s","egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"},{"rule":"gate-members"}],"gid":1000,"hardening":"restricted","identity":"release-gate","identityToken":true,"image":"ghcr.io/jorisjonkers-dev/delivery/release-gate@sha256:3e7a9c1d5b2f8e4a6c0d9b7e5f3a1c8d6e4b2a0f9c7e5d3b1a8f6e4c2d0b9a7e","imageVersion":"1.0.0","ingress":[{"consumes":"release-gate.http","port":8080,"rule":"consumer"}],"lifecycle":"application","liveness":{"failures":3,"path":"/healthz","period":"10s","port":8080,"timeout":"5s"},"memory":"64Mi","name":"release-gate","placement":{"eligibleNodes":["enschede-t1000-1","enschede-rx7900xtx-1","enschede-gtx-960m-1","enschede-pi-1","enschede-pi-2","enschede-pi-3","frankfurt-contabo-1"]},"priorityClass":"platform-critical-app","readiness":{"failures":3,"path":"/readyz","period":"10s","port":8080,"timeout":"5s"},"replicas":1,"runtime":"none","startup":{"failures":2,"path":"/healthz","period":"5s","port":8080,"timeout":"5s"},"surfaces":[{"name":"http","port":8080}],"switchover":"rolling","uid":1000}],"project":"delivery","provenance":{"inputDigests":[{"digest":"sha256:ebb5134deeb55f320e28609054a0aca85a359f3869501a3a8b7560d7d05a4277","input":"intent-fragment","name":"delivery"},{"digest":"sha256:9a29fae42c0c6cd4f9824d7af3d9ecc68c068a3a00d1f9488bfbb70c882ea565","input":"intent-fragment","name":"edge"},{"digest":"sha256:c45963dcb3911475bff7d4fd17c0ae570230bdadba4ff2a68c75272d9219e762","input":"intent-fragment","name":"identity"},{"digest":"sha256:447e1500b09b608cecc0f93ebb598a60e655690822ecc63395e4e40eaadb98cb","input":"intent-fragment","name":"mail"},{"digest":"sha256:35d5e3643093422537ea72def0f4b19e93ed319a2d0192fc7846342970ce8797","input":"intent-fragment","name":"notes"},{"digest":"sha256:5916815b3b70a0de96137a6614f9356003985016bbf078bafe098433c9aab7d1","input":"intent-fragment","name":"observability"},{"digest":"sha256:31309996835674cc4f037f8e19897ddbe4a7fa8a487a3efc613e251caeed0853","input":"intent-fragment","name":"secrets"},{"digest":"sha256:f7e7e1e0a33d9528e11d956c36b5a6b8f37f758fb98aecdd74ea3eaf7286d60e","input":"platform-intent","name":"jorisjonkers.dev"},{"digest":"sha256:a989e80337efb294fa1a7175b3ad3eea8bed9bf13fbd503a26691c9265575fc2","input":"node-contract","name":"production"},{"digest":"sha256:f09f02097230414af40101e8ebe053e62d679c085a7d1fed9d860c8ba151b68b","input":"images-lock","name":"estate"},{"digest":"sha256:202e5e2a22ab20cfe5b6e85cbd8139cb13ed3b199b2f1af4380068d21d54ff1f","input":"cluster-state","name":"production"}],"renderHash":"sha256:c944fc023ec1bda7d6df2150acd36374ae0d7f8aae46e54baf8c2ec48f1e2f1a","schemaPackageIntegrity":"sha256:5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b"},"reconcileUnit":"apps-delivery","revision":"sha256:fd5cf8e44420b0bb2ead36b1bcdf4b5f02d3e93a6703fe37eb4ff4dd998994fd"} \ No newline at end of file diff --git a/spec/v1/examples/delivery/rendered/apps/delivery/release-gate/networkpolicy.yaml b/spec/v1/examples/delivery/rendered/apps/delivery/release-gate/networkpolicy.yaml index 1dbc31fe4..9043497c8 100644 --- a/spec/v1/examples/delivery/rendered/apps/delivery/release-gate/networkpolicy.yaml +++ b/spec/v1/examples/delivery/rendered/apps/delivery/release-gate/networkpolicy.yaml @@ -39,6 +39,13 @@ spec: port: 53 - protocol: TCP port: 53 + - to: + - namespaceSelector: + matchLabels: + app.kubernetes.io/managed-by: deploy-kit + podSelector: + matchLabels: + gated.deploy-kit.jorisjonkers.dev/release-gate: "true" - to: - ipBlock: cidr: 10.43.0.1/32 diff --git a/spec/v1/examples/identity/expected/resolved.grafana.json b/spec/v1/examples/identity/expected/resolved.grafana.json index 819e55e46..035e73cbb 100644 --- a/spec/v1/examples/identity/expected/resolved.grafana.json +++ b/spec/v1/examples/identity/expected/resolved.grafana.json @@ -1 +1 @@ -{"apiVersion":"resolved.jorisjonkers.dev/v1","client":{"credential":"secret","description":"Metrics and dashboards","host":"grafana.jorisjonkers.dev","iconUrl":"https://grafana.jorisjonkers.dev/public/img/grafana_icon.svg","label":"Grafana","launchUrl":"https://grafana.jorisjonkers.dev/","redirectUris":["https://grafana.jorisjonkers.dev/login/generic_oauth"],"roles":["user","viewer","editor","admin"],"secretPath":"secret/data/identity-system/grafana/client","signIn":"native"},"exposure":[{"certificates":"acme","dns":{"proxied":true,"target":"ingress.jorisjonkers.dev"},"host":"grafana.jorisjonkers.dev","ingressClass":"traefik-public","listener":"tls","name":"public","proxy":{"application":"traefik-public","namespace":"edge-system"},"routes":[{"audience":"authenticated","match":"prefix","middleware":[{"kind":"security-headers"}],"path":"/","precedence":1,"process":"grafana","surface":"http"}],"tier":"public-frankfurt"}],"id":"grafana","kind":"ResolvedApplication","namespace":"identity-system","processes":[{"cpu":"50m","cutover":"continuous","deadline":"210s","egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"},{"namespace":"secrets-system","port":8200,"process":"vault","rule":"secret-store"}],"environment":[{"name":"GF_AUTH_DISABLE_LOGIN_FORM","value":"true"},{"name":"GF_AUTH_GENERIC_OAUTH_API_URL","value":"https://id.jorisjonkers.dev/userinfo"},{"name":"GF_AUTH_GENERIC_OAUTH_AUTH_URL","value":"https://id.jorisjonkers.dev/oauth2/authorize"},{"name":"GF_AUTH_GENERIC_OAUTH_CLIENT_ID","value":"grafana"},{"name":"GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET","secret":{"key":"secret","path":"secret/data/identity-system/grafana/client"}},{"name":"GF_AUTH_GENERIC_OAUTH_ENABLED","value":"true"},{"name":"GF_AUTH_GENERIC_OAUTH_NAME","value":"jorisjonkers.dev"},{"name":"GF_AUTH_GENERIC_OAUTH_SCOPES","value":"openid,profile,email"},{"name":"GF_AUTH_GENERIC_OAUTH_TOKEN_URL","value":"https://id.jorisjonkers.dev/oauth2/token"},{"name":"GF_SERVER_ROOT_URL","value":"https://grafana.jorisjonkers.dev"}],"gid":472,"hardening":"restricted","identity":"grafana","identityToken":false,"image":"docker.io/grafana/grafana@sha256:2c4e6a8b0d2f4c6e8a0b2d4f6c8e0a2b4d6f8c0e2a4b6d8f0c2e4a6b8d0f2c4e","imageVersion":"12.2.0","ingress":[{"namespace":"edge-system","port":3000,"process":"traefik-public","rule":"tier-proxy"},{"consumes":"grafana.http","port":3000,"rule":"consumer"}],"lifecycle":"application","memory":"256Mi","name":"grafana","placement":{"eligibleNodes":["enschede-t1000-1","enschede-rx7900xtx-1","enschede-gtx-960m-1","enschede-pi-1","enschede-pi-2","enschede-pi-3","frankfurt-contabo-1"]},"readiness":{"failures":3,"path":"/api/health","period":"10s","port":3000,"timeout":"5s"},"replicas":1,"runtime":"none","secrets":[{"access":"read","delivery":"env","destination":"grafana-identity-system-grafana-client","keys":["id","secret"],"path":"secret/data/identity-system/grafana/client","restartTargets":["grafana"]}],"surfaces":[{"name":"http","port":3000}],"switchover":"blue-green","uid":472,"writablePaths":[{"path":"/var/lib/grafana","size":"64Mi"}]}],"project":"identity","provenance":{"inputDigests":[{"digest":"sha256:ebb5134deeb55f320e28609054a0aca85a359f3869501a3a8b7560d7d05a4277","input":"intent-fragment","name":"delivery"},{"digest":"sha256:9a29fae42c0c6cd4f9824d7af3d9ecc68c068a3a00d1f9488bfbb70c882ea565","input":"intent-fragment","name":"edge"},{"digest":"sha256:c45963dcb3911475bff7d4fd17c0ae570230bdadba4ff2a68c75272d9219e762","input":"intent-fragment","name":"identity"},{"digest":"sha256:447e1500b09b608cecc0f93ebb598a60e655690822ecc63395e4e40eaadb98cb","input":"intent-fragment","name":"mail"},{"digest":"sha256:35d5e3643093422537ea72def0f4b19e93ed319a2d0192fc7846342970ce8797","input":"intent-fragment","name":"notes"},{"digest":"sha256:5916815b3b70a0de96137a6614f9356003985016bbf078bafe098433c9aab7d1","input":"intent-fragment","name":"observability"},{"digest":"sha256:31309996835674cc4f037f8e19897ddbe4a7fa8a487a3efc613e251caeed0853","input":"intent-fragment","name":"secrets"},{"digest":"sha256:f7e7e1e0a33d9528e11d956c36b5a6b8f37f758fb98aecdd74ea3eaf7286d60e","input":"platform-intent","name":"jorisjonkers.dev"},{"digest":"sha256:a989e80337efb294fa1a7175b3ad3eea8bed9bf13fbd503a26691c9265575fc2","input":"node-contract","name":"production"},{"digest":"sha256:f09f02097230414af40101e8ebe053e62d679c085a7d1fed9d860c8ba151b68b","input":"images-lock","name":"estate"},{"digest":"sha256:202e5e2a22ab20cfe5b6e85cbd8139cb13ed3b199b2f1af4380068d21d54ff1f","input":"cluster-state","name":"production"}],"renderHash":"sha256:c944fc023ec1bda7d6df2150acd36374ae0d7f8aae46e54baf8c2ec48f1e2f1a","schemaPackageIntegrity":"sha256:5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b"},"reconcileAfter":["apps-edge","apps-secrets"],"reconcileUnit":"apps-identity","releaseGate":{"analysis":{"errorRate":"1%","interval":"30s","iterations":4,"latency":"500ms","threshold":3},"deadline":"210s","endpoint":"http://release-gate.delivery-system.svc.cluster.local:8080","members":[{"process":"grafana","readiness":{"path":"/api/health","port":3000}}],"protectedPath":"/api/user"},"revision":"sha256:8cd64f25a1a68f005aec0360fe297741345ebaf5c2b9080ff231dfc3f3df0c9d","secretStore":"http://vault.secrets-system.svc.cluster.local:8200"} \ No newline at end of file +{"apiVersion":"resolved.jorisjonkers.dev/v1","client":{"credential":"secret","description":"Metrics and dashboards","host":"grafana.jorisjonkers.dev","iconUrl":"https://grafana.jorisjonkers.dev/public/img/grafana_icon.svg","label":"Grafana","launchUrl":"https://grafana.jorisjonkers.dev/","redirectUris":["https://grafana.jorisjonkers.dev/login/generic_oauth"],"roles":["user","viewer","editor","admin"],"secretPath":"secret/data/identity-system/grafana/client","signIn":"native"},"exposure":[{"certificates":"acme","dns":{"proxied":true,"target":"ingress.jorisjonkers.dev"},"host":"grafana.jorisjonkers.dev","ingressClass":"traefik-public","listener":"tls","name":"public","proxy":{"application":"traefik-public","namespace":"edge-system"},"routes":[{"audience":"authenticated","match":"prefix","middleware":[{"kind":"security-headers"}],"path":"/","precedence":1,"process":"grafana","surface":"http"}],"tier":"public-frankfurt"}],"id":"grafana","kind":"ResolvedApplication","namespace":"identity-system","processes":[{"cpu":"50m","cutover":"continuous","deadline":"210s","egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"},{"namespace":"secrets-system","port":8200,"process":"vault","rule":"secret-store"}],"environment":[{"name":"GF_AUTH_DISABLE_LOGIN_FORM","value":"true"},{"name":"GF_AUTH_GENERIC_OAUTH_API_URL","value":"https://id.jorisjonkers.dev/userinfo"},{"name":"GF_AUTH_GENERIC_OAUTH_AUTH_URL","value":"https://id.jorisjonkers.dev/oauth2/authorize"},{"name":"GF_AUTH_GENERIC_OAUTH_CLIENT_ID","value":"grafana"},{"name":"GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET","secret":{"key":"secret","path":"secret/data/identity-system/grafana/client"}},{"name":"GF_AUTH_GENERIC_OAUTH_ENABLED","value":"true"},{"name":"GF_AUTH_GENERIC_OAUTH_NAME","value":"jorisjonkers.dev"},{"name":"GF_AUTH_GENERIC_OAUTH_SCOPES","value":"openid,profile,email"},{"name":"GF_AUTH_GENERIC_OAUTH_TOKEN_URL","value":"https://id.jorisjonkers.dev/oauth2/token"},{"name":"GF_SERVER_ROOT_URL","value":"https://grafana.jorisjonkers.dev"}],"gid":472,"hardening":"restricted","identity":"grafana","identityToken":false,"image":"docker.io/grafana/grafana@sha256:2c4e6a8b0d2f4c6e8a0b2d4f6c8e0a2b4d6f8c0e2a4b6d8f0c2e4a6b8d0f2c4e","imageVersion":"12.2.0","ingress":[{"namespace":"edge-system","port":3000,"process":"traefik-public","rule":"tier-proxy"},{"consumes":"grafana.http","port":3000,"rule":"consumer"},{"namespace":"delivery-system","port":3000,"process":"release-gate","rule":"release-gate"}],"lifecycle":"application","memory":"256Mi","name":"grafana","placement":{"eligibleNodes":["enschede-t1000-1","enschede-rx7900xtx-1","enschede-gtx-960m-1","enschede-pi-1","enschede-pi-2","enschede-pi-3","frankfurt-contabo-1"]},"readiness":{"failures":3,"path":"/api/health","period":"10s","port":3000,"timeout":"5s"},"replicas":1,"runtime":"none","secrets":[{"access":"read","delivery":"env","destination":"grafana-identity-system-grafana-client","keys":["id","secret"],"path":"secret/data/identity-system/grafana/client","restartTargets":["grafana"]}],"surfaces":[{"name":"http","port":3000}],"switchover":"blue-green","uid":472,"writablePaths":[{"path":"/var/lib/grafana","size":"64Mi"}]}],"project":"identity","provenance":{"inputDigests":[{"digest":"sha256:ebb5134deeb55f320e28609054a0aca85a359f3869501a3a8b7560d7d05a4277","input":"intent-fragment","name":"delivery"},{"digest":"sha256:9a29fae42c0c6cd4f9824d7af3d9ecc68c068a3a00d1f9488bfbb70c882ea565","input":"intent-fragment","name":"edge"},{"digest":"sha256:c45963dcb3911475bff7d4fd17c0ae570230bdadba4ff2a68c75272d9219e762","input":"intent-fragment","name":"identity"},{"digest":"sha256:447e1500b09b608cecc0f93ebb598a60e655690822ecc63395e4e40eaadb98cb","input":"intent-fragment","name":"mail"},{"digest":"sha256:35d5e3643093422537ea72def0f4b19e93ed319a2d0192fc7846342970ce8797","input":"intent-fragment","name":"notes"},{"digest":"sha256:5916815b3b70a0de96137a6614f9356003985016bbf078bafe098433c9aab7d1","input":"intent-fragment","name":"observability"},{"digest":"sha256:31309996835674cc4f037f8e19897ddbe4a7fa8a487a3efc613e251caeed0853","input":"intent-fragment","name":"secrets"},{"digest":"sha256:f7e7e1e0a33d9528e11d956c36b5a6b8f37f758fb98aecdd74ea3eaf7286d60e","input":"platform-intent","name":"jorisjonkers.dev"},{"digest":"sha256:a989e80337efb294fa1a7175b3ad3eea8bed9bf13fbd503a26691c9265575fc2","input":"node-contract","name":"production"},{"digest":"sha256:f09f02097230414af40101e8ebe053e62d679c085a7d1fed9d860c8ba151b68b","input":"images-lock","name":"estate"},{"digest":"sha256:202e5e2a22ab20cfe5b6e85cbd8139cb13ed3b199b2f1af4380068d21d54ff1f","input":"cluster-state","name":"production"}],"renderHash":"sha256:c944fc023ec1bda7d6df2150acd36374ae0d7f8aae46e54baf8c2ec48f1e2f1a","schemaPackageIntegrity":"sha256:5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b"},"reconcileAfter":["apps-edge","apps-secrets"],"reconcileUnit":"apps-identity","releaseGate":{"analysis":{"errorRate":"1%","interval":"30s","iterations":4,"latency":"500ms","threshold":3},"deadline":"210s","endpoint":"http://release-gate.delivery-system.svc.cluster.local:8080","members":[{"process":"grafana","readiness":{"path":"/api/health","port":3000}}],"protectedPath":"/api/user"},"revision":"sha256:09c3571e7ffddafbaa594cb2b21bdd5c54fa81cfe59398c36b85bb9ddac47c85","secretStore":"http://vault.secrets-system.svc.cluster.local:8200"} \ No newline at end of file diff --git a/spec/v1/examples/identity/expected/resolved.identity.json b/spec/v1/examples/identity/expected/resolved.identity.json index d32abe716..8692f1101 100644 --- a/spec/v1/examples/identity/expected/resolved.identity.json +++ b/spec/v1/examples/identity/expected/resolved.identity.json @@ -1 +1 @@ -{"apiVersion":"resolved.jorisjonkers.dev/v1","exposure":[{"certificates":"acme","dns":{"proxied":true,"target":"ingress.jorisjonkers.dev"},"host":"id.jorisjonkers.dev","ingressClass":"traefik-public","listener":"tls","name":"public","proxy":{"application":"traefik-public","namespace":"edge-system"},"routes":[{"audience":"anonymous","match":"prefix","middleware":[{"contentPolicy":"strict","kind":"security-headers"}],"path":"/","precedence":1,"process":"identity","surface":"http"}],"tier":"public-frankfurt"}],"id":"identity","kind":"ResolvedApplication","namespace":"identity-system","processes":[{"cpu":"50m","cutover":"continuous","deadline":"210s","egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"},{"namespace":"secrets-system","port":8200,"process":"vault","rule":"secret-store"}],"gid":65532,"hardening":"restricted","identity":"identity","identityToken":true,"image":"ghcr.io/jorisjonkers-dev/identity/identity@sha256:1a3c5e7b9d1f3a5c7e9b1d3f5a7c9e1b3d5f7a9c1e3b5d7f9a1c3e5b7d9f1a3c","imageVersion":"1.0.0","ingress":[{"namespace":"edge-system","port":8080,"process":"traefik-public","rule":"tier-proxy"},{"consumes":"identity.http","port":8080,"rule":"consumer"}],"lifecycle":"application","liveness":{"failures":3,"path":"/health/live","period":"10s","port":8080,"timeout":"5s"},"memory":"256Mi","name":"identity","placement":{"eligibleNodes":["enschede-t1000-1","enschede-rx7900xtx-1","enschede-gtx-960m-1","enschede-pi-1","enschede-pi-2","enschede-pi-3","frankfurt-contabo-1"]},"priorityClass":"platform-critical-app","readiness":{"failures":3,"path":"/health/ready","period":"10s","port":8080,"timeout":"5s"},"replicas":1,"runtime":"none","secrets":[{"client":"grafana","delivery":"self","paths":[{"allows":["create","read","update"],"path":"secret/data/identity-system/grafana/client"}]}],"startup":{"failures":6,"path":"/health/live","period":"5s","port":8080,"timeout":"5s"},"surfaces":[{"name":"http","port":8080}],"switchover":"blue-green","uid":65532}],"project":"identity","provenance":{"inputDigests":[{"digest":"sha256:ebb5134deeb55f320e28609054a0aca85a359f3869501a3a8b7560d7d05a4277","input":"intent-fragment","name":"delivery"},{"digest":"sha256:9a29fae42c0c6cd4f9824d7af3d9ecc68c068a3a00d1f9488bfbb70c882ea565","input":"intent-fragment","name":"edge"},{"digest":"sha256:c45963dcb3911475bff7d4fd17c0ae570230bdadba4ff2a68c75272d9219e762","input":"intent-fragment","name":"identity"},{"digest":"sha256:447e1500b09b608cecc0f93ebb598a60e655690822ecc63395e4e40eaadb98cb","input":"intent-fragment","name":"mail"},{"digest":"sha256:35d5e3643093422537ea72def0f4b19e93ed319a2d0192fc7846342970ce8797","input":"intent-fragment","name":"notes"},{"digest":"sha256:5916815b3b70a0de96137a6614f9356003985016bbf078bafe098433c9aab7d1","input":"intent-fragment","name":"observability"},{"digest":"sha256:31309996835674cc4f037f8e19897ddbe4a7fa8a487a3efc613e251caeed0853","input":"intent-fragment","name":"secrets"},{"digest":"sha256:f7e7e1e0a33d9528e11d956c36b5a6b8f37f758fb98aecdd74ea3eaf7286d60e","input":"platform-intent","name":"jorisjonkers.dev"},{"digest":"sha256:a989e80337efb294fa1a7175b3ad3eea8bed9bf13fbd503a26691c9265575fc2","input":"node-contract","name":"production"},{"digest":"sha256:f09f02097230414af40101e8ebe053e62d679c085a7d1fed9d860c8ba151b68b","input":"images-lock","name":"estate"},{"digest":"sha256:202e5e2a22ab20cfe5b6e85cbd8139cb13ed3b199b2f1af4380068d21d54ff1f","input":"cluster-state","name":"production"}],"renderHash":"sha256:c944fc023ec1bda7d6df2150acd36374ae0d7f8aae46e54baf8c2ec48f1e2f1a","schemaPackageIntegrity":"sha256:5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b"},"reconcileAfter":["apps-edge","apps-secrets"],"reconcileUnit":"apps-identity","releaseGate":{"analysis":{"errorRate":"1%","interval":"30s","iterations":4,"latency":"500ms","threshold":3},"deadline":"210s","endpoint":"http://release-gate.delivery-system.svc.cluster.local:8080","members":[{"process":"identity","readiness":{"path":"/health/ready","port":8080}}]},"revision":"sha256:606797ba5edc909ce6aebb3b50b96ed1cd7d81a685b978352c6b5f5f0a4b772b","secretStore":"http://vault.secrets-system.svc.cluster.local:8200"} \ No newline at end of file +{"apiVersion":"resolved.jorisjonkers.dev/v1","exposure":[{"certificates":"acme","dns":{"proxied":true,"target":"ingress.jorisjonkers.dev"},"host":"id.jorisjonkers.dev","ingressClass":"traefik-public","listener":"tls","name":"public","proxy":{"application":"traefik-public","namespace":"edge-system"},"routes":[{"audience":"anonymous","match":"prefix","middleware":[{"contentPolicy":"strict","kind":"security-headers"}],"path":"/","precedence":1,"process":"identity","surface":"http"}],"tier":"public-frankfurt"}],"id":"identity","kind":"ResolvedApplication","namespace":"identity-system","processes":[{"cpu":"50m","cutover":"continuous","deadline":"210s","egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"},{"namespace":"secrets-system","port":8200,"process":"vault","rule":"secret-store"}],"gid":65532,"hardening":"restricted","identity":"identity","identityToken":true,"image":"ghcr.io/jorisjonkers-dev/identity/identity@sha256:1a3c5e7b9d1f3a5c7e9b1d3f5a7c9e1b3d5f7a9c1e3b5d7f9a1c3e5b7d9f1a3c","imageVersion":"1.0.0","ingress":[{"namespace":"edge-system","port":8080,"process":"traefik-public","rule":"tier-proxy"},{"consumes":"identity.http","port":8080,"rule":"consumer"},{"namespace":"delivery-system","port":8080,"process":"release-gate","rule":"release-gate"}],"lifecycle":"application","liveness":{"failures":3,"path":"/health/live","period":"10s","port":8080,"timeout":"5s"},"memory":"256Mi","name":"identity","placement":{"eligibleNodes":["enschede-t1000-1","enschede-rx7900xtx-1","enschede-gtx-960m-1","enschede-pi-1","enschede-pi-2","enschede-pi-3","frankfurt-contabo-1"]},"priorityClass":"platform-critical-app","readiness":{"failures":3,"path":"/health/ready","period":"10s","port":8080,"timeout":"5s"},"replicas":1,"runtime":"none","secrets":[{"client":"grafana","delivery":"self","paths":[{"allows":["create","read","update"],"path":"secret/data/identity-system/grafana/client"}]}],"startup":{"failures":6,"path":"/health/live","period":"5s","port":8080,"timeout":"5s"},"surfaces":[{"name":"http","port":8080}],"switchover":"blue-green","uid":65532}],"project":"identity","provenance":{"inputDigests":[{"digest":"sha256:ebb5134deeb55f320e28609054a0aca85a359f3869501a3a8b7560d7d05a4277","input":"intent-fragment","name":"delivery"},{"digest":"sha256:9a29fae42c0c6cd4f9824d7af3d9ecc68c068a3a00d1f9488bfbb70c882ea565","input":"intent-fragment","name":"edge"},{"digest":"sha256:c45963dcb3911475bff7d4fd17c0ae570230bdadba4ff2a68c75272d9219e762","input":"intent-fragment","name":"identity"},{"digest":"sha256:447e1500b09b608cecc0f93ebb598a60e655690822ecc63395e4e40eaadb98cb","input":"intent-fragment","name":"mail"},{"digest":"sha256:35d5e3643093422537ea72def0f4b19e93ed319a2d0192fc7846342970ce8797","input":"intent-fragment","name":"notes"},{"digest":"sha256:5916815b3b70a0de96137a6614f9356003985016bbf078bafe098433c9aab7d1","input":"intent-fragment","name":"observability"},{"digest":"sha256:31309996835674cc4f037f8e19897ddbe4a7fa8a487a3efc613e251caeed0853","input":"intent-fragment","name":"secrets"},{"digest":"sha256:f7e7e1e0a33d9528e11d956c36b5a6b8f37f758fb98aecdd74ea3eaf7286d60e","input":"platform-intent","name":"jorisjonkers.dev"},{"digest":"sha256:a989e80337efb294fa1a7175b3ad3eea8bed9bf13fbd503a26691c9265575fc2","input":"node-contract","name":"production"},{"digest":"sha256:f09f02097230414af40101e8ebe053e62d679c085a7d1fed9d860c8ba151b68b","input":"images-lock","name":"estate"},{"digest":"sha256:202e5e2a22ab20cfe5b6e85cbd8139cb13ed3b199b2f1af4380068d21d54ff1f","input":"cluster-state","name":"production"}],"renderHash":"sha256:c944fc023ec1bda7d6df2150acd36374ae0d7f8aae46e54baf8c2ec48f1e2f1a","schemaPackageIntegrity":"sha256:5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b"},"reconcileAfter":["apps-edge","apps-secrets"],"reconcileUnit":"apps-identity","releaseGate":{"analysis":{"errorRate":"1%","interval":"30s","iterations":4,"latency":"500ms","threshold":3},"deadline":"210s","endpoint":"http://release-gate.delivery-system.svc.cluster.local:8080","members":[{"process":"identity","readiness":{"path":"/health/ready","port":8080}}]},"revision":"sha256:3b14b1fc52b1e48002fbad4ece53c5885b7f0036594f00869d92ee9037b2b215","secretStore":"http://vault.secrets-system.svc.cluster.local:8200"} \ No newline at end of file diff --git a/spec/v1/examples/identity/expected/resolved.portal.json b/spec/v1/examples/identity/expected/resolved.portal.json index d8fc02840..6ce48c9fc 100644 --- a/spec/v1/examples/identity/expected/resolved.portal.json +++ b/spec/v1/examples/identity/expected/resolved.portal.json @@ -1 +1 @@ -{"apiVersion":"resolved.jorisjonkers.dev/v1","client":{"credential":"pkce","description":"Every application you can open","host":"home.jorisjonkers.dev","label":"Home","launchUrl":"https://home.jorisjonkers.dev/apps","redirectUris":["https://home.jorisjonkers.dev/callback","https://home.jorisjonkers.dev/silent-renew"],"roles":["user"],"signIn":"native"},"exposure":[{"certificates":"acme","dns":{"proxied":true,"target":"ingress.jorisjonkers.dev"},"host":"home.jorisjonkers.dev","ingressClass":"traefik-public","listener":"tls","name":"public","proxy":{"application":"traefik-public","namespace":"edge-system"},"routes":[{"audience":"authenticated","match":"prefix","middleware":[{"kind":"security-headers"}],"path":"/","precedence":1,"process":"portal","surface":"http"}],"tier":"public-frankfurt"}],"id":"portal","kind":"ResolvedApplication","namespace":"identity-system","processes":[{"cpu":"10m","cutover":"continuous","deadline":"720s","egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"}],"gid":101,"hardening":"restricted","identity":"portal","identityToken":false,"image":"ghcr.io/jorisjonkers-dev/identity/portal@sha256:3d5f7b9c1e3a5d7f9b1c3e5a7d9f1b3c5e7a9d1f3b5c7e9a1d3f5b7c9e1a3d5f","imageVersion":"1.0.0","ingress":[{"namespace":"edge-system","port":8080,"process":"traefik-public","rule":"tier-proxy"},{"consumes":"portal.http","port":8080,"rule":"consumer"}],"lifecycle":"application","memory":"64Mi","name":"portal","placement":{"eligibleNodes":["enschede-t1000-1","enschede-rx7900xtx-1","enschede-gtx-960m-1","enschede-pi-1","enschede-pi-2","enschede-pi-3","frankfurt-contabo-1"]},"readiness":{"failures":3,"path":"/","period":"10s","port":8080,"timeout":"5s"},"replicas":1,"runtime":"none","surfaces":[{"name":"http","port":8080}],"switchover":"blue-green","uid":101}],"project":"identity","provenance":{"inputDigests":[{"digest":"sha256:ebb5134deeb55f320e28609054a0aca85a359f3869501a3a8b7560d7d05a4277","input":"intent-fragment","name":"delivery"},{"digest":"sha256:9a29fae42c0c6cd4f9824d7af3d9ecc68c068a3a00d1f9488bfbb70c882ea565","input":"intent-fragment","name":"edge"},{"digest":"sha256:c45963dcb3911475bff7d4fd17c0ae570230bdadba4ff2a68c75272d9219e762","input":"intent-fragment","name":"identity"},{"digest":"sha256:447e1500b09b608cecc0f93ebb598a60e655690822ecc63395e4e40eaadb98cb","input":"intent-fragment","name":"mail"},{"digest":"sha256:35d5e3643093422537ea72def0f4b19e93ed319a2d0192fc7846342970ce8797","input":"intent-fragment","name":"notes"},{"digest":"sha256:5916815b3b70a0de96137a6614f9356003985016bbf078bafe098433c9aab7d1","input":"intent-fragment","name":"observability"},{"digest":"sha256:31309996835674cc4f037f8e19897ddbe4a7fa8a487a3efc613e251caeed0853","input":"intent-fragment","name":"secrets"},{"digest":"sha256:f7e7e1e0a33d9528e11d956c36b5a6b8f37f758fb98aecdd74ea3eaf7286d60e","input":"platform-intent","name":"jorisjonkers.dev"},{"digest":"sha256:a989e80337efb294fa1a7175b3ad3eea8bed9bf13fbd503a26691c9265575fc2","input":"node-contract","name":"production"},{"digest":"sha256:f09f02097230414af40101e8ebe053e62d679c085a7d1fed9d860c8ba151b68b","input":"images-lock","name":"estate"},{"digest":"sha256:202e5e2a22ab20cfe5b6e85cbd8139cb13ed3b199b2f1af4380068d21d54ff1f","input":"cluster-state","name":"production"}],"renderHash":"sha256:c944fc023ec1bda7d6df2150acd36374ae0d7f8aae46e54baf8c2ec48f1e2f1a","schemaPackageIntegrity":"sha256:5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b"},"reconcileAfter":["apps-edge"],"reconcileUnit":"apps-identity","releaseGate":{"analysis":{"errorRate":"1%","interval":"30s","iterations":4,"latency":"500ms","threshold":3},"deadline":"720s","endpoint":"http://release-gate.delivery-system.svc.cluster.local:8080","members":[{"process":"portal","readiness":{"path":"/","port":8080}}],"protectedPath":"/api/me"},"revision":"sha256:bd4ec7935b72e30287503e1d358b3173cb86badb6b5dd3389cc5d127bd0a660b"} \ No newline at end of file +{"apiVersion":"resolved.jorisjonkers.dev/v1","client":{"credential":"pkce","description":"Every application you can open","host":"home.jorisjonkers.dev","label":"Home","launchUrl":"https://home.jorisjonkers.dev/apps","redirectUris":["https://home.jorisjonkers.dev/callback","https://home.jorisjonkers.dev/silent-renew"],"roles":["user"],"signIn":"native"},"exposure":[{"certificates":"acme","dns":{"proxied":true,"target":"ingress.jorisjonkers.dev"},"host":"home.jorisjonkers.dev","ingressClass":"traefik-public","listener":"tls","name":"public","proxy":{"application":"traefik-public","namespace":"edge-system"},"routes":[{"audience":"authenticated","match":"prefix","middleware":[{"kind":"security-headers"}],"path":"/","precedence":1,"process":"portal","surface":"http"}],"tier":"public-frankfurt"}],"id":"portal","kind":"ResolvedApplication","namespace":"identity-system","processes":[{"cpu":"10m","cutover":"continuous","deadline":"720s","egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"}],"gid":101,"hardening":"restricted","identity":"portal","identityToken":false,"image":"ghcr.io/jorisjonkers-dev/identity/portal@sha256:3d5f7b9c1e3a5d7f9b1c3e5a7d9f1b3c5e7a9d1f3b5c7e9a1d3f5b7c9e1a3d5f","imageVersion":"1.0.0","ingress":[{"namespace":"edge-system","port":8080,"process":"traefik-public","rule":"tier-proxy"},{"consumes":"portal.http","port":8080,"rule":"consumer"},{"namespace":"delivery-system","port":8080,"process":"release-gate","rule":"release-gate"}],"lifecycle":"application","memory":"64Mi","name":"portal","placement":{"eligibleNodes":["enschede-t1000-1","enschede-rx7900xtx-1","enschede-gtx-960m-1","enschede-pi-1","enschede-pi-2","enschede-pi-3","frankfurt-contabo-1"]},"readiness":{"failures":3,"path":"/","period":"10s","port":8080,"timeout":"5s"},"replicas":1,"runtime":"none","surfaces":[{"name":"http","port":8080}],"switchover":"blue-green","uid":101}],"project":"identity","provenance":{"inputDigests":[{"digest":"sha256:ebb5134deeb55f320e28609054a0aca85a359f3869501a3a8b7560d7d05a4277","input":"intent-fragment","name":"delivery"},{"digest":"sha256:9a29fae42c0c6cd4f9824d7af3d9ecc68c068a3a00d1f9488bfbb70c882ea565","input":"intent-fragment","name":"edge"},{"digest":"sha256:c45963dcb3911475bff7d4fd17c0ae570230bdadba4ff2a68c75272d9219e762","input":"intent-fragment","name":"identity"},{"digest":"sha256:447e1500b09b608cecc0f93ebb598a60e655690822ecc63395e4e40eaadb98cb","input":"intent-fragment","name":"mail"},{"digest":"sha256:35d5e3643093422537ea72def0f4b19e93ed319a2d0192fc7846342970ce8797","input":"intent-fragment","name":"notes"},{"digest":"sha256:5916815b3b70a0de96137a6614f9356003985016bbf078bafe098433c9aab7d1","input":"intent-fragment","name":"observability"},{"digest":"sha256:31309996835674cc4f037f8e19897ddbe4a7fa8a487a3efc613e251caeed0853","input":"intent-fragment","name":"secrets"},{"digest":"sha256:f7e7e1e0a33d9528e11d956c36b5a6b8f37f758fb98aecdd74ea3eaf7286d60e","input":"platform-intent","name":"jorisjonkers.dev"},{"digest":"sha256:a989e80337efb294fa1a7175b3ad3eea8bed9bf13fbd503a26691c9265575fc2","input":"node-contract","name":"production"},{"digest":"sha256:f09f02097230414af40101e8ebe053e62d679c085a7d1fed9d860c8ba151b68b","input":"images-lock","name":"estate"},{"digest":"sha256:202e5e2a22ab20cfe5b6e85cbd8139cb13ed3b199b2f1af4380068d21d54ff1f","input":"cluster-state","name":"production"}],"renderHash":"sha256:c944fc023ec1bda7d6df2150acd36374ae0d7f8aae46e54baf8c2ec48f1e2f1a","schemaPackageIntegrity":"sha256:5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b"},"reconcileAfter":["apps-edge"],"reconcileUnit":"apps-identity","releaseGate":{"analysis":{"errorRate":"1%","interval":"30s","iterations":4,"latency":"500ms","threshold":3},"deadline":"720s","endpoint":"http://release-gate.delivery-system.svc.cluster.local:8080","members":[{"process":"portal","readiness":{"path":"/","port":8080}}],"protectedPath":"/api/me"},"revision":"sha256:6d89e7db6bf21aae3ae646fff88197c6034fc8f85f2a124c5ec07ea3c911ac47"} \ No newline at end of file diff --git a/spec/v1/examples/identity/expected/resolved.whoami.json b/spec/v1/examples/identity/expected/resolved.whoami.json index 8b11f90d5..edb30350c 100644 --- a/spec/v1/examples/identity/expected/resolved.whoami.json +++ b/spec/v1/examples/identity/expected/resolved.whoami.json @@ -1 +1 @@ -{"apiVersion":"resolved.jorisjonkers.dev/v1","client":{"host":"whoami.jorisjonkers.dev","iconUrl":"https://whoami.jorisjonkers.dev/favicon.ico","label":"Who am I","launchUrl":"https://whoami.jorisjonkers.dev/","roles":["user"],"signIn":"gated"},"exposure":[{"certificates":"acme","dns":{"proxied":true,"target":"ingress.jorisjonkers.dev"},"host":"whoami.jorisjonkers.dev","ingressClass":"traefik-public","listener":"tls","name":"public","proxy":{"application":"traefik-public","namespace":"edge-system"},"routes":[{"audience":"authenticated","match":"prefix","middleware":[{"endpoint":"http://identity.identity-system.svc.cluster.local:8080/gate/forward","kind":"forward-auth"},{"kind":"security-headers"}],"path":"/","precedence":3,"process":"whoami","surface":"http"},{"audience":"anonymous","match":"exact","middleware":[{"kind":"security-headers"}],"path":"/health","precedence":1,"process":"whoami","surface":"http"},{"audience":"authenticated","gate":{"name":"identity","namespace":"identity-system","port":8080},"match":"prefix","middleware":[{"kind":"security-headers"}],"path":"/.auth/","precedence":2}],"tier":"public-frankfurt"}],"id":"whoami","kind":"ResolvedApplication","namespace":"identity-system","processes":[{"cpu":"10m","cutover":"continuous","deadline":"720s","egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"}],"gid":65532,"hardening":"restricted","identity":"whoami","identityToken":false,"image":"docker.io/traefik/whoami@sha256:4e6a8c0d2f4b6e8a0c2d4f6b8e0a2c4d6f8b0e2a4c6d8f0b2e4a6c8d0f2b4e6a","imageVersion":"v1.11.0","ingress":[{"namespace":"edge-system","port":8080,"process":"traefik-public","rule":"tier-proxy"},{"consumes":"whoami.http","port":8080,"rule":"consumer"}],"lifecycle":"application","memory":"32Mi","name":"whoami","placement":{"eligibleNodes":["enschede-t1000-1","enschede-rx7900xtx-1","enschede-gtx-960m-1","enschede-pi-1","enschede-pi-2","enschede-pi-3","frankfurt-contabo-1"]},"readiness":{"failures":3,"path":"/health","period":"10s","port":8080,"timeout":"5s"},"replicas":2,"runtime":"none","spreadAcross":"node","surfaces":[{"name":"http","port":8080}],"switchover":"blue-green","uid":65532}],"project":"identity","provenance":{"inputDigests":[{"digest":"sha256:ebb5134deeb55f320e28609054a0aca85a359f3869501a3a8b7560d7d05a4277","input":"intent-fragment","name":"delivery"},{"digest":"sha256:9a29fae42c0c6cd4f9824d7af3d9ecc68c068a3a00d1f9488bfbb70c882ea565","input":"intent-fragment","name":"edge"},{"digest":"sha256:c45963dcb3911475bff7d4fd17c0ae570230bdadba4ff2a68c75272d9219e762","input":"intent-fragment","name":"identity"},{"digest":"sha256:447e1500b09b608cecc0f93ebb598a60e655690822ecc63395e4e40eaadb98cb","input":"intent-fragment","name":"mail"},{"digest":"sha256:35d5e3643093422537ea72def0f4b19e93ed319a2d0192fc7846342970ce8797","input":"intent-fragment","name":"notes"},{"digest":"sha256:5916815b3b70a0de96137a6614f9356003985016bbf078bafe098433c9aab7d1","input":"intent-fragment","name":"observability"},{"digest":"sha256:31309996835674cc4f037f8e19897ddbe4a7fa8a487a3efc613e251caeed0853","input":"intent-fragment","name":"secrets"},{"digest":"sha256:f7e7e1e0a33d9528e11d956c36b5a6b8f37f758fb98aecdd74ea3eaf7286d60e","input":"platform-intent","name":"jorisjonkers.dev"},{"digest":"sha256:a989e80337efb294fa1a7175b3ad3eea8bed9bf13fbd503a26691c9265575fc2","input":"node-contract","name":"production"},{"digest":"sha256:f09f02097230414af40101e8ebe053e62d679c085a7d1fed9d860c8ba151b68b","input":"images-lock","name":"estate"},{"digest":"sha256:202e5e2a22ab20cfe5b6e85cbd8139cb13ed3b199b2f1af4380068d21d54ff1f","input":"cluster-state","name":"production"}],"renderHash":"sha256:c944fc023ec1bda7d6df2150acd36374ae0d7f8aae46e54baf8c2ec48f1e2f1a","schemaPackageIntegrity":"sha256:5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b"},"reconcileAfter":["apps-edge"],"reconcileUnit":"apps-identity","releaseGate":{"analysis":{"errorRate":"1%","interval":"30s","iterations":4,"latency":"500ms","threshold":3},"deadline":"720s","endpoint":"http://release-gate.delivery-system.svc.cluster.local:8080","members":[{"process":"whoami","readiness":{"path":"/health","port":8080}}]},"revision":"sha256:75a01aa72abc6100d2601807bdab25ce68b8335611bcc274b66b7beb8d8c5646"} \ No newline at end of file +{"apiVersion":"resolved.jorisjonkers.dev/v1","client":{"host":"whoami.jorisjonkers.dev","iconUrl":"https://whoami.jorisjonkers.dev/favicon.ico","label":"Who am I","launchUrl":"https://whoami.jorisjonkers.dev/","roles":["user"],"signIn":"gated"},"exposure":[{"certificates":"acme","dns":{"proxied":true,"target":"ingress.jorisjonkers.dev"},"host":"whoami.jorisjonkers.dev","ingressClass":"traefik-public","listener":"tls","name":"public","proxy":{"application":"traefik-public","namespace":"edge-system"},"routes":[{"audience":"authenticated","match":"prefix","middleware":[{"endpoint":"http://identity.identity-system.svc.cluster.local:8080/gate/forward","kind":"forward-auth"},{"kind":"security-headers"}],"path":"/","precedence":3,"process":"whoami","surface":"http"},{"audience":"anonymous","match":"exact","middleware":[{"kind":"security-headers"}],"path":"/health","precedence":1,"process":"whoami","surface":"http"},{"audience":"authenticated","gate":{"name":"identity","namespace":"identity-system","port":8080},"match":"prefix","middleware":[{"kind":"security-headers"}],"path":"/.auth/","precedence":2}],"tier":"public-frankfurt"}],"id":"whoami","kind":"ResolvedApplication","namespace":"identity-system","processes":[{"cpu":"10m","cutover":"continuous","deadline":"720s","egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"}],"gid":65532,"hardening":"restricted","identity":"whoami","identityToken":false,"image":"docker.io/traefik/whoami@sha256:4e6a8c0d2f4b6e8a0c2d4f6b8e0a2c4d6f8b0e2a4c6d8f0b2e4a6c8d0f2b4e6a","imageVersion":"v1.11.0","ingress":[{"namespace":"edge-system","port":8080,"process":"traefik-public","rule":"tier-proxy"},{"consumes":"whoami.http","port":8080,"rule":"consumer"},{"namespace":"delivery-system","port":8080,"process":"release-gate","rule":"release-gate"}],"lifecycle":"application","memory":"32Mi","name":"whoami","placement":{"eligibleNodes":["enschede-t1000-1","enschede-rx7900xtx-1","enschede-gtx-960m-1","enschede-pi-1","enschede-pi-2","enschede-pi-3","frankfurt-contabo-1"]},"readiness":{"failures":3,"path":"/health","period":"10s","port":8080,"timeout":"5s"},"replicas":2,"runtime":"none","spreadAcross":"node","surfaces":[{"name":"http","port":8080}],"switchover":"blue-green","uid":65532}],"project":"identity","provenance":{"inputDigests":[{"digest":"sha256:ebb5134deeb55f320e28609054a0aca85a359f3869501a3a8b7560d7d05a4277","input":"intent-fragment","name":"delivery"},{"digest":"sha256:9a29fae42c0c6cd4f9824d7af3d9ecc68c068a3a00d1f9488bfbb70c882ea565","input":"intent-fragment","name":"edge"},{"digest":"sha256:c45963dcb3911475bff7d4fd17c0ae570230bdadba4ff2a68c75272d9219e762","input":"intent-fragment","name":"identity"},{"digest":"sha256:447e1500b09b608cecc0f93ebb598a60e655690822ecc63395e4e40eaadb98cb","input":"intent-fragment","name":"mail"},{"digest":"sha256:35d5e3643093422537ea72def0f4b19e93ed319a2d0192fc7846342970ce8797","input":"intent-fragment","name":"notes"},{"digest":"sha256:5916815b3b70a0de96137a6614f9356003985016bbf078bafe098433c9aab7d1","input":"intent-fragment","name":"observability"},{"digest":"sha256:31309996835674cc4f037f8e19897ddbe4a7fa8a487a3efc613e251caeed0853","input":"intent-fragment","name":"secrets"},{"digest":"sha256:f7e7e1e0a33d9528e11d956c36b5a6b8f37f758fb98aecdd74ea3eaf7286d60e","input":"platform-intent","name":"jorisjonkers.dev"},{"digest":"sha256:a989e80337efb294fa1a7175b3ad3eea8bed9bf13fbd503a26691c9265575fc2","input":"node-contract","name":"production"},{"digest":"sha256:f09f02097230414af40101e8ebe053e62d679c085a7d1fed9d860c8ba151b68b","input":"images-lock","name":"estate"},{"digest":"sha256:202e5e2a22ab20cfe5b6e85cbd8139cb13ed3b199b2f1af4380068d21d54ff1f","input":"cluster-state","name":"production"}],"renderHash":"sha256:c944fc023ec1bda7d6df2150acd36374ae0d7f8aae46e54baf8c2ec48f1e2f1a","schemaPackageIntegrity":"sha256:5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b"},"reconcileAfter":["apps-edge"],"reconcileUnit":"apps-identity","releaseGate":{"analysis":{"errorRate":"1%","interval":"30s","iterations":4,"latency":"500ms","threshold":3},"deadline":"720s","endpoint":"http://release-gate.delivery-system.svc.cluster.local:8080","members":[{"process":"whoami","readiness":{"path":"/health","port":8080}}]},"revision":"sha256:056ec0528c4316481b926e76eeb44272923b7ad379f2c98f22db90474c40aa8d"} \ No newline at end of file diff --git a/spec/v1/examples/identity/rendered/apps/identity/grafana/canary.yaml b/spec/v1/examples/identity/rendered/apps/identity/grafana/canary.yaml index 7d5ceb712..bfa11b5eb 100644 --- a/spec/v1/examples/identity/rendered/apps/identity/grafana/canary.yaml +++ b/spec/v1/examples/identity/rendered/apps/identity/grafana/canary.yaml @@ -33,7 +33,7 @@ spec: metadata: application: grafana process: grafana - revision: sha256:8cd64f25a1a68f005aec0360fe297741345ebaf5c2b9080ff231dfc3f3df0c9d + revision: sha256:09c3571e7ffddafbaa594cb2b21bdd5c54fa81cfe59398c36b85bb9ddac47c85 paths: / readiness: /api/health hostnames: grafana.jorisjonkers.dev @@ -45,7 +45,7 @@ spec: metadata: application: grafana process: grafana - revision: sha256:8cd64f25a1a68f005aec0360fe297741345ebaf5c2b9080ff231dfc3f3df0c9d + revision: sha256:09c3571e7ffddafbaa594cb2b21bdd5c54fa81cfe59398c36b85bb9ddac47c85 paths: / readiness: /api/health hostnames: grafana.jorisjonkers.dev @@ -57,7 +57,7 @@ spec: metadata: application: grafana process: grafana - revision: sha256:8cd64f25a1a68f005aec0360fe297741345ebaf5c2b9080ff231dfc3f3df0c9d + revision: sha256:09c3571e7ffddafbaa594cb2b21bdd5c54fa81cfe59398c36b85bb9ddac47c85 paths: / readiness: /api/health hostnames: grafana.jorisjonkers.dev diff --git a/spec/v1/examples/identity/rendered/apps/identity/grafana/networkpolicy.yaml b/spec/v1/examples/identity/rendered/apps/identity/grafana/networkpolicy.yaml index 5af587180..c0d322af5 100644 --- a/spec/v1/examples/identity/rendered/apps/identity/grafana/networkpolicy.yaml +++ b/spec/v1/examples/identity/rendered/apps/identity/grafana/networkpolicy.yaml @@ -39,6 +39,16 @@ spec: ports: - protocol: TCP port: 3000 + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: delivery-system + podSelector: + matchLabels: + app.kubernetes.io/instance: release-gate + ports: + - protocol: TCP + port: 3000 egress: - to: - namespaceSelector: diff --git a/spec/v1/examples/identity/rendered/apps/identity/grafana/vso.yaml b/spec/v1/examples/identity/rendered/apps/identity/grafana/vso.yaml index e0de0e03a..6fbc83085 100644 --- a/spec/v1/examples/identity/rendered/apps/identity/grafana/vso.yaml +++ b/spec/v1/examples/identity/rendered/apps/identity/grafana/vso.yaml @@ -12,7 +12,7 @@ metadata: app.kubernetes.io/managed-by: deploy-kit app.kubernetes.io/component: none annotations: - estate.jorisjonkers.dev/revision: sha256:8cd64f25a1a68f005aec0360fe297741345ebaf5c2b9080ff231dfc3f3df0c9d + estate.jorisjonkers.dev/revision: sha256:09c3571e7ffddafbaa594cb2b21bdd5c54fa81cfe59398c36b85bb9ddac47c85 kustomize.toolkit.fluxcd.io/prune: disabled spec: vaultConnectionRef: secret-store @@ -34,7 +34,7 @@ metadata: app.kubernetes.io/managed-by: deploy-kit app.kubernetes.io/component: none annotations: - estate.jorisjonkers.dev/revision: sha256:8cd64f25a1a68f005aec0360fe297741345ebaf5c2b9080ff231dfc3f3df0c9d + estate.jorisjonkers.dev/revision: sha256:09c3571e7ffddafbaa594cb2b21bdd5c54fa81cfe59398c36b85bb9ddac47c85 kustomize.toolkit.fluxcd.io/prune: disabled spec: vaultAuthRef: grafana diff --git a/spec/v1/examples/identity/rendered/apps/identity/grafana/workload.yaml b/spec/v1/examples/identity/rendered/apps/identity/grafana/workload.yaml index 63eb7f897..1ebdb0727 100644 --- a/spec/v1/examples/identity/rendered/apps/identity/grafana/workload.yaml +++ b/spec/v1/examples/identity/rendered/apps/identity/grafana/workload.yaml @@ -30,6 +30,7 @@ spec: app.kubernetes.io/part-of: grafana app.kubernetes.io/managed-by: deploy-kit app.kubernetes.io/component: none + gated.deploy-kit.jorisjonkers.dev/release-gate: "true" annotations: image.jorisjonkers.dev/grafana: 12.2.0 spec: diff --git a/spec/v1/examples/identity/rendered/apps/identity/identity/canary.yaml b/spec/v1/examples/identity/rendered/apps/identity/identity/canary.yaml index 25e7661c9..bff0a4b45 100644 --- a/spec/v1/examples/identity/rendered/apps/identity/identity/canary.yaml +++ b/spec/v1/examples/identity/rendered/apps/identity/identity/canary.yaml @@ -33,7 +33,7 @@ spec: metadata: application: identity process: identity - revision: sha256:606797ba5edc909ce6aebb3b50b96ed1cd7d81a685b978352c6b5f5f0a4b772b + revision: sha256:3b14b1fc52b1e48002fbad4ece53c5885b7f0036594f00869d92ee9037b2b215 paths: / readiness: /health/ready hostnames: id.jorisjonkers.dev @@ -45,7 +45,7 @@ spec: metadata: application: identity process: identity - revision: sha256:606797ba5edc909ce6aebb3b50b96ed1cd7d81a685b978352c6b5f5f0a4b772b + revision: sha256:3b14b1fc52b1e48002fbad4ece53c5885b7f0036594f00869d92ee9037b2b215 paths: / readiness: /health/ready hostnames: id.jorisjonkers.dev @@ -57,7 +57,7 @@ spec: metadata: application: identity process: identity - revision: sha256:606797ba5edc909ce6aebb3b50b96ed1cd7d81a685b978352c6b5f5f0a4b772b + revision: sha256:3b14b1fc52b1e48002fbad4ece53c5885b7f0036594f00869d92ee9037b2b215 paths: / readiness: /health/ready hostnames: id.jorisjonkers.dev diff --git a/spec/v1/examples/identity/rendered/apps/identity/identity/networkpolicy.yaml b/spec/v1/examples/identity/rendered/apps/identity/identity/networkpolicy.yaml index 1a156c0d7..f4bfe8d4e 100644 --- a/spec/v1/examples/identity/rendered/apps/identity/identity/networkpolicy.yaml +++ b/spec/v1/examples/identity/rendered/apps/identity/identity/networkpolicy.yaml @@ -39,6 +39,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: diff --git a/spec/v1/examples/identity/rendered/apps/identity/identity/workload.yaml b/spec/v1/examples/identity/rendered/apps/identity/identity/workload.yaml index e5880a0b3..4a8f42163 100644 --- a/spec/v1/examples/identity/rendered/apps/identity/identity/workload.yaml +++ b/spec/v1/examples/identity/rendered/apps/identity/identity/workload.yaml @@ -30,6 +30,7 @@ spec: app.kubernetes.io/part-of: identity app.kubernetes.io/managed-by: deploy-kit app.kubernetes.io/component: none + gated.deploy-kit.jorisjonkers.dev/release-gate: "true" annotations: image.jorisjonkers.dev/identity: 1.0.0 spec: diff --git a/spec/v1/examples/identity/rendered/apps/identity/portal/canary.yaml b/spec/v1/examples/identity/rendered/apps/identity/portal/canary.yaml index 3a1565931..aab0bb66d 100644 --- a/spec/v1/examples/identity/rendered/apps/identity/portal/canary.yaml +++ b/spec/v1/examples/identity/rendered/apps/identity/portal/canary.yaml @@ -33,7 +33,7 @@ spec: metadata: application: portal process: portal - revision: sha256:bd4ec7935b72e30287503e1d358b3173cb86badb6b5dd3389cc5d127bd0a660b + revision: sha256:6d89e7db6bf21aae3ae646fff88197c6034fc8f85f2a124c5ec07ea3c911ac47 paths: / readiness: / hostnames: home.jorisjonkers.dev @@ -45,7 +45,7 @@ spec: metadata: application: portal process: portal - revision: sha256:bd4ec7935b72e30287503e1d358b3173cb86badb6b5dd3389cc5d127bd0a660b + revision: sha256:6d89e7db6bf21aae3ae646fff88197c6034fc8f85f2a124c5ec07ea3c911ac47 paths: / readiness: / hostnames: home.jorisjonkers.dev @@ -57,7 +57,7 @@ spec: metadata: application: portal process: portal - revision: sha256:bd4ec7935b72e30287503e1d358b3173cb86badb6b5dd3389cc5d127bd0a660b + revision: sha256:6d89e7db6bf21aae3ae646fff88197c6034fc8f85f2a124c5ec07ea3c911ac47 paths: / readiness: / hostnames: home.jorisjonkers.dev diff --git a/spec/v1/examples/identity/rendered/apps/identity/portal/networkpolicy.yaml b/spec/v1/examples/identity/rendered/apps/identity/portal/networkpolicy.yaml index d78ab4ade..6cfa89637 100644 --- a/spec/v1/examples/identity/rendered/apps/identity/portal/networkpolicy.yaml +++ b/spec/v1/examples/identity/rendered/apps/identity/portal/networkpolicy.yaml @@ -39,6 +39,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: diff --git a/spec/v1/examples/identity/rendered/apps/identity/portal/workload.yaml b/spec/v1/examples/identity/rendered/apps/identity/portal/workload.yaml index b9a966c02..64e275230 100644 --- a/spec/v1/examples/identity/rendered/apps/identity/portal/workload.yaml +++ b/spec/v1/examples/identity/rendered/apps/identity/portal/workload.yaml @@ -30,6 +30,7 @@ spec: app.kubernetes.io/part-of: portal app.kubernetes.io/managed-by: deploy-kit app.kubernetes.io/component: none + gated.deploy-kit.jorisjonkers.dev/release-gate: "true" annotations: image.jorisjonkers.dev/portal: 1.0.0 spec: diff --git a/spec/v1/examples/identity/rendered/apps/identity/whoami/canary.yaml b/spec/v1/examples/identity/rendered/apps/identity/whoami/canary.yaml index 378212426..a2ca8862a 100644 --- a/spec/v1/examples/identity/rendered/apps/identity/whoami/canary.yaml +++ b/spec/v1/examples/identity/rendered/apps/identity/whoami/canary.yaml @@ -37,7 +37,7 @@ spec: metadata: application: whoami process: whoami - revision: sha256:75a01aa72abc6100d2601807bdab25ce68b8335611bcc274b66b7beb8d8c5646 + revision: sha256:056ec0528c4316481b926e76eeb44272923b7ad379f2c98f22db90474c40aa8d paths: / /health readiness: /health hostnames: whoami.jorisjonkers.dev @@ -49,7 +49,7 @@ spec: metadata: application: whoami process: whoami - revision: sha256:75a01aa72abc6100d2601807bdab25ce68b8335611bcc274b66b7beb8d8c5646 + revision: sha256:056ec0528c4316481b926e76eeb44272923b7ad379f2c98f22db90474c40aa8d paths: / /health readiness: /health hostnames: whoami.jorisjonkers.dev @@ -61,7 +61,7 @@ spec: metadata: application: whoami process: whoami - revision: sha256:75a01aa72abc6100d2601807bdab25ce68b8335611bcc274b66b7beb8d8c5646 + revision: sha256:056ec0528c4316481b926e76eeb44272923b7ad379f2c98f22db90474c40aa8d paths: / /health readiness: /health hostnames: whoami.jorisjonkers.dev diff --git a/spec/v1/examples/identity/rendered/apps/identity/whoami/networkpolicy.yaml b/spec/v1/examples/identity/rendered/apps/identity/whoami/networkpolicy.yaml index a175b9ad3..6b2b90b3b 100644 --- a/spec/v1/examples/identity/rendered/apps/identity/whoami/networkpolicy.yaml +++ b/spec/v1/examples/identity/rendered/apps/identity/whoami/networkpolicy.yaml @@ -39,6 +39,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: diff --git a/spec/v1/examples/identity/rendered/apps/identity/whoami/workload.yaml b/spec/v1/examples/identity/rendered/apps/identity/whoami/workload.yaml index 9b457d030..ec303d844 100644 --- a/spec/v1/examples/identity/rendered/apps/identity/whoami/workload.yaml +++ b/spec/v1/examples/identity/rendered/apps/identity/whoami/workload.yaml @@ -30,6 +30,7 @@ spec: app.kubernetes.io/part-of: whoami app.kubernetes.io/managed-by: deploy-kit app.kubernetes.io/component: none + gated.deploy-kit.jorisjonkers.dev/release-gate: "true" annotations: image.jorisjonkers.dev/whoami: v1.11.0 spec: diff --git a/spec/v1/examples/knowledge-platform/expected/resolved.hindsight.json b/spec/v1/examples/knowledge-platform/expected/resolved.hindsight.json index 0f2bd1b53..9c33eb6eb 100644 --- a/spec/v1/examples/knowledge-platform/expected/resolved.hindsight.json +++ b/spec/v1/examples/knowledge-platform/expected/resolved.hindsight.json @@ -1 +1 @@ -{"apiVersion":"resolved.jorisjonkers.dev/v1","exposure":[{"certificates":"acme","dns":{"proxied":true,"target":"ingress.jorisjonkers.dev"},"host":"memory.jorisjonkers.dev","ingressClass":"traefik-public","listener":"tls","name":"console","proxy":{"application":"traefik-public","namespace":"edge-system"},"routes":[{"audience":"authenticated","match":"prefix","middleware":[{"endpoint":"http://identity.identity-system.svc.cluster.local:8080/gate/forward","kind":"forward-auth"},{"contentPolicy":"strict","kind":"security-headers"}],"path":"/","precedence":1,"process":"hindsight-control-plane","surface":"console"}],"tier":"public-frankfurt"},{"certificates":"acme","dns":{"proxied":true,"target":"ingress.jorisjonkers.dev"},"host":"memory-api.jorisjonkers.dev","ingressClass":"traefik-public","listener":"tls","name":"api","proxy":{"application":"traefik-public","namespace":"edge-system"},"routes":[{"audience":"authenticated","match":"prefix","middleware":[{"endpoint":"http://identity.identity-system.svc.cluster.local:8080/gate/forward","kind":"forward-auth"},{"contentPolicy":"strict","kind":"security-headers"}],"path":"/","precedence":1,"process":"hindsight-api","surface":"api"}],"tier":"public-frankfurt"}],"id":"hindsight","kind":"ResolvedApplication","namespace":"knowledge-platform-system","processes":[{"consumes":["platform-postgres.postgres","hindsight-embedding.embeddings"],"cpu":"500m","cutover":"continuous","deadline":"480s","dependencies":[{"address":"platform-postgres:5432","application":"platform-postgres","peers":[{"namespace":"data-system","port":5432,"process":"postgres"}],"surface":"postgres","target":"postgres.data-system.svc.cluster.local"},{"address":"hindsight-tei-embedding:8091","application":"hindsight-embedding","peers":[{"namespace":"knowledge-platform-system","port":8091,"process":"hindsight-tei-embedding"}],"surface":"embeddings"}],"egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"},{"namespace":"secrets-system","port":8200,"process":"vault","rule":"secret-store"}],"environment":[{"name":"DEPLOYMENT_ENVIRONMENT","value":"production"},{"name":"HINDSIGHT_API_EMBEDDINGS_PROVIDER","value":"tei"},{"name":"HINDSIGHT_API_LLM_API_KEY","secret":{"key":"llm-api-key","path":"secret/data/platform/hindsight"}},{"name":"HINDSIGHT_API_LLM_BASE_URL","value":"https://eu.openrouter.ai/api/v1"},{"name":"HINDSIGHT_API_LLM_MODEL","value":"mistralai/mistral-small-2603"},{"name":"HINDSIGHT_API_LLM_PROVIDER","value":"openai"},{"name":"HINDSIGHT_API_PORT","value":"8888"},{"name":"HINDSIGHT_API_WORKER_ENABLED","value":"false"},{"name":"OTEL_EXPORTER_OTLP_ENDPOINT","value":"http://otel-collector.observability-system.svc.cluster.local:4317"},{"name":"OTEL_SERVICE_NAME","value":"hindsight-api"},{"name":"PGPASSWORD","secret":{"key":"password","path":"database/creds/knowledge-platform-owner"}},{"name":"PGUSER","secret":{"key":"username","path":"database/creds/knowledge-platform-owner"}},{"name":"PORT","value":"8888"}],"gid":1000,"hardening":"restricted","identity":"hindsight-api","identityToken":false,"image":"ghcr.io/vectorize-io/hindsight-api@sha256:7b14a1f4062252992d0176758753615e0a2071d9a269995be007be223ab01812","imageVersion":"0.4.0","ingress":[{"namespace":"edge-system","port":8888,"process":"traefik-public","rule":"tier-proxy"},{"consumes":"hindsight.api","port":8888,"rule":"consumer"}],"lifecycle":"application","liveness":{"failures":3,"path":"/health/live","period":"10s","port":8888,"timeout":"5s"},"memory":"1Gi","name":"hindsight-api","placement":{"eligibleNodes":["enschede-t1000-1","enschede-rx7900xtx-1","enschede-gtx-960m-1","enschede-pi-1","enschede-pi-2","enschede-pi-3"]},"readiness":{"failures":3,"path":"/health","period":"10s","port":8888,"timeout":"5s"},"replicas":1,"runtime":"python","secrets":[{"access":"read","delivery":"env","destination":"hindsight-api-platform-hindsight","keys":["llm-api-key"],"path":"secret/data/platform/hindsight","restartTargets":["hindsight-api"]},{"delivery":"env","destination":"hindsight-api-database-creds-knowledge-platform-owner","engine":"database","paths":[{"allows":["read"],"path":"database/creds/knowledge-platform-owner"}],"restartTargets":["hindsight-api"]}],"startup":{"failures":24,"path":"/health/live","period":"5s","port":8888,"timeout":"5s"},"surfaces":[{"name":"api","port":8888}],"switchover":"blue-green","uid":1000},{"consumes":["platform-postgres.postgres","hindsight-embedding.embeddings"],"cpu":"500m","cutover":"continuous","deadline":"480s","dependencies":[{"address":"platform-postgres:5432","application":"platform-postgres","peers":[{"namespace":"data-system","port":5432,"process":"postgres"}],"surface":"postgres","target":"postgres.data-system.svc.cluster.local"},{"address":"hindsight-tei-embedding:8091","application":"hindsight-embedding","peers":[{"namespace":"knowledge-platform-system","port":8091,"process":"hindsight-tei-embedding"}],"surface":"embeddings"}],"egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"},{"namespace":"secrets-system","port":8200,"process":"vault","rule":"secret-store"}],"environment":[{"name":"DEPLOYMENT_ENVIRONMENT","value":"production"},{"name":"HINDSIGHT_API_EMBEDDINGS_PROVIDER","value":"tei"},{"name":"HINDSIGHT_API_LLM_API_KEY","secret":{"key":"llm-api-key","path":"secret/data/platform/hindsight"}},{"name":"HINDSIGHT_API_LLM_BASE_URL","value":"https://eu.openrouter.ai/api/v1"},{"name":"HINDSIGHT_API_LLM_MODEL","value":"mistralai/mistral-small-2603"},{"name":"HINDSIGHT_API_LLM_PROVIDER","value":"openai"},{"name":"HINDSIGHT_API_PORT","value":"8889"},{"name":"HINDSIGHT_API_WORKER_BATCH_SIZE","value":"10"},{"name":"HINDSIGHT_API_WORKER_HTTP_PORT","value":"8889"},{"name":"HINDSIGHT_API_WORKER_MAX_RETRIES","value":"3"},{"name":"HINDSIGHT_API_WORKER_POLL_INTERVAL_MS","value":"500"},{"name":"OTEL_EXPORTER_OTLP_ENDPOINT","value":"http://otel-collector.observability-system.svc.cluster.local:4317"},{"name":"OTEL_SERVICE_NAME","value":"hindsight-worker"},{"name":"PORT","value":"8889"}],"gid":1000,"hardening":"restricted","identity":"hindsight-worker","identityToken":false,"image":"ghcr.io/vectorize-io/hindsight-api@sha256:7b14a1f4062252992d0176758753615e0a2071d9a269995be007be223ab01812","imageVersion":"0.4.0","ingress":[{"consumes":"hindsight.worker","port":8889,"rule":"consumer"}],"lifecycle":"application","liveness":{"failures":3,"path":"/health/live","period":"10s","port":8889,"timeout":"5s"},"memory":"1Gi","name":"hindsight-worker","parked":true,"placement":{"eligibleNodes":["enschede-t1000-1","enschede-rx7900xtx-1","enschede-gtx-960m-1","enschede-pi-1","enschede-pi-2","enschede-pi-3"]},"readiness":{"failures":3,"path":"/health","period":"10s","port":8889,"timeout":"5s"},"replicas":0,"runtime":"python","secrets":[{"access":"read","delivery":"env","destination":"hindsight-worker-platform-hindsight","keys":["llm-api-key"],"path":"secret/data/platform/hindsight","restartTargets":["hindsight-worker"]},{"delivery":"file","destination":"hindsight-worker-database-creds-knowledge-platform-owner","engine":"database","fileMode":"0400","mountAt":"/run/secrets/database","paths":[{"allows":["read"],"path":"database/creds/knowledge-platform-owner"}],"restartTargets":["hindsight-worker"]}],"startup":{"failures":24,"path":"/health/live","period":"5s","port":8889,"timeout":"5s"},"surfaces":[{"name":"worker","port":8889}],"switchover":"stop-start","uid":1000},{"consumes":["hindsight.api"],"cpu":"250m","cutover":"continuous","deadline":"300s","dependencies":[{"address":"hindsight-api:8888","application":"hindsight","peers":[{"namespace":"knowledge-platform-system","port":8888,"process":"hindsight-api"}],"surface":"api"}],"egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"}],"environment":[{"name":"DEPLOYMENT_ENVIRONMENT","value":"production"},{"name":"HINDSIGHT_CP_HOSTNAME","value":"0.0.0.0"},{"name":"HINDSIGHT_CP_PORT","value":"3000"},{"name":"NODE_ENV","value":"production"},{"name":"OTEL_EXPORTER_OTLP_ENDPOINT","value":"http://otel-collector.observability-system.svc.cluster.local:4317"},{"name":"OTEL_SERVICE_NAME","value":"hindsight-control-plane"},{"name":"PORT","value":"3000"}],"gid":1000,"hardening":"restricted","identity":"hindsight-control-plane","identityToken":false,"image":"ghcr.io/vectorize-io/hindsight-control-plane@sha256:d9bf57083f7cb0a53ee5011bdfde6aae2490dbf5c360d2d1bff10473b45eb112","imageVersion":"0.4.0","ingress":[{"namespace":"edge-system","port":3000,"process":"traefik-public","rule":"tier-proxy"},{"consumes":"hindsight.console","port":3000,"rule":"consumer"}],"lifecycle":"application","liveness":{"failures":3,"period":"10s","tcp":3000,"timeout":"5s"},"memory":"512Mi","name":"hindsight-control-plane","placement":{"eligibleNodes":["enschede-t1000-1","enschede-rx7900xtx-1","enschede-gtx-960m-1","enschede-pi-1","enschede-pi-2","enschede-pi-3"]},"readiness":{"failures":3,"period":"10s","tcp":3000,"timeout":"5s"},"replicas":1,"runtime":"node","startup":{"failures":12,"period":"5s","tcp":3000,"timeout":"5s"},"surfaces":[{"name":"console","port":3000}],"switchover":"blue-green","uid":1000}],"project":"knowledge-platform","provenance":{"inputDigests":[{"digest":"sha256:5391c90578e87c9e5a3697dd7f32268e92aa578786ba33a2f82ebe50b392a42e","input":"intent-fragment","name":"data"},{"digest":"sha256:ebb5134deeb55f320e28609054a0aca85a359f3869501a3a8b7560d7d05a4277","input":"intent-fragment","name":"delivery"},{"digest":"sha256:9a29fae42c0c6cd4f9824d7af3d9ecc68c068a3a00d1f9488bfbb70c882ea565","input":"intent-fragment","name":"edge"},{"digest":"sha256:c45963dcb3911475bff7d4fd17c0ae570230bdadba4ff2a68c75272d9219e762","input":"intent-fragment","name":"identity"},{"digest":"sha256:50542ac63f880a0818a38f350b0cf5e0b3cd6ced6021f85ce98b9d1fdde63f27","input":"intent-fragment","name":"knowledge-platform"},{"digest":"sha256:447e1500b09b608cecc0f93ebb598a60e655690822ecc63395e4e40eaadb98cb","input":"intent-fragment","name":"mail"},{"digest":"sha256:5916815b3b70a0de96137a6614f9356003985016bbf078bafe098433c9aab7d1","input":"intent-fragment","name":"observability"},{"digest":"sha256:31309996835674cc4f037f8e19897ddbe4a7fa8a487a3efc613e251caeed0853","input":"intent-fragment","name":"secrets"},{"digest":"sha256:f7e7e1e0a33d9528e11d956c36b5a6b8f37f758fb98aecdd74ea3eaf7286d60e","input":"platform-intent","name":"jorisjonkers.dev"},{"digest":"sha256:a989e80337efb294fa1a7175b3ad3eea8bed9bf13fbd503a26691c9265575fc2","input":"node-contract","name":"production"},{"digest":"sha256:f09f02097230414af40101e8ebe053e62d679c085a7d1fed9d860c8ba151b68b","input":"images-lock","name":"estate"},{"digest":"sha256:202e5e2a22ab20cfe5b6e85cbd8139cb13ed3b199b2f1af4380068d21d54ff1f","input":"cluster-state","name":"production"}],"renderHash":"sha256:817217a81d3ab49b3381bc31ebad23f0258563587ca65ed4a3dd41ae99665346","schemaPackageIntegrity":"sha256:5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b"},"reconcileAfter":["apps-data","apps-edge","apps-secrets"],"reconcileUnit":"apps-knowledge-platform","releaseGate":{"analysis":{"errorRate":"1%","interval":"30s","iterations":4,"latency":"500ms","threshold":3},"deadline":"480s","endpoint":"http://release-gate.delivery-system.svc.cluster.local:8080","members":[{"checks":["error-rate","latency"],"process":"hindsight-api","readiness":{"path":"/health","port":8888},"waitsFor":[{"application":"hindsight-embedding","process":"hindsight-tei-embedding"}]},{"checks":["error-rate","latency"],"process":"hindsight-control-plane","readiness":{"tcp":3000}}]},"revision":"sha256:932b57848c01da8ff277900e56678d27f4f8199d36c5e1a68f59012b6a4a5de6","secretStore":"http://vault.secrets-system.svc.cluster.local:8200"} \ No newline at end of file +{"apiVersion":"resolved.jorisjonkers.dev/v1","exposure":[{"certificates":"acme","dns":{"proxied":true,"target":"ingress.jorisjonkers.dev"},"host":"memory.jorisjonkers.dev","ingressClass":"traefik-public","listener":"tls","name":"console","proxy":{"application":"traefik-public","namespace":"edge-system"},"routes":[{"audience":"authenticated","match":"prefix","middleware":[{"endpoint":"http://identity.identity-system.svc.cluster.local:8080/gate/forward","kind":"forward-auth"},{"contentPolicy":"strict","kind":"security-headers"}],"path":"/","precedence":1,"process":"hindsight-control-plane","surface":"console"}],"tier":"public-frankfurt"},{"certificates":"acme","dns":{"proxied":true,"target":"ingress.jorisjonkers.dev"},"host":"memory-api.jorisjonkers.dev","ingressClass":"traefik-public","listener":"tls","name":"api","proxy":{"application":"traefik-public","namespace":"edge-system"},"routes":[{"audience":"authenticated","match":"prefix","middleware":[{"endpoint":"http://identity.identity-system.svc.cluster.local:8080/gate/forward","kind":"forward-auth"},{"contentPolicy":"strict","kind":"security-headers"}],"path":"/","precedence":1,"process":"hindsight-api","surface":"api"}],"tier":"public-frankfurt"}],"id":"hindsight","kind":"ResolvedApplication","namespace":"knowledge-platform-system","processes":[{"consumes":["platform-postgres.postgres","hindsight-embedding.embeddings"],"cpu":"500m","cutover":"continuous","deadline":"480s","dependencies":[{"address":"platform-postgres:5432","application":"platform-postgres","peers":[{"namespace":"data-system","port":5432,"process":"postgres"}],"surface":"postgres","target":"postgres.data-system.svc.cluster.local"},{"address":"hindsight-tei-embedding:8091","application":"hindsight-embedding","peers":[{"namespace":"knowledge-platform-system","port":8091,"process":"hindsight-tei-embedding"}],"surface":"embeddings"}],"egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"},{"namespace":"secrets-system","port":8200,"process":"vault","rule":"secret-store"}],"environment":[{"name":"DEPLOYMENT_ENVIRONMENT","value":"production"},{"name":"HINDSIGHT_API_EMBEDDINGS_PROVIDER","value":"tei"},{"name":"HINDSIGHT_API_LLM_API_KEY","secret":{"key":"llm-api-key","path":"secret/data/platform/hindsight"}},{"name":"HINDSIGHT_API_LLM_BASE_URL","value":"https://eu.openrouter.ai/api/v1"},{"name":"HINDSIGHT_API_LLM_MODEL","value":"mistralai/mistral-small-2603"},{"name":"HINDSIGHT_API_LLM_PROVIDER","value":"openai"},{"name":"HINDSIGHT_API_PORT","value":"8888"},{"name":"HINDSIGHT_API_WORKER_ENABLED","value":"false"},{"name":"OTEL_EXPORTER_OTLP_ENDPOINT","value":"http://otel-collector.observability-system.svc.cluster.local:4317"},{"name":"OTEL_SERVICE_NAME","value":"hindsight-api"},{"name":"PGPASSWORD","secret":{"key":"password","path":"database/creds/knowledge-platform-owner"}},{"name":"PGUSER","secret":{"key":"username","path":"database/creds/knowledge-platform-owner"}},{"name":"PORT","value":"8888"}],"gid":1000,"hardening":"restricted","identity":"hindsight-api","identityToken":false,"image":"ghcr.io/vectorize-io/hindsight-api@sha256:7b14a1f4062252992d0176758753615e0a2071d9a269995be007be223ab01812","imageVersion":"0.4.0","ingress":[{"namespace":"edge-system","port":8888,"process":"traefik-public","rule":"tier-proxy"},{"consumes":"hindsight.api","port":8888,"rule":"consumer"},{"namespace":"delivery-system","port":8888,"process":"release-gate","rule":"release-gate"}],"lifecycle":"application","liveness":{"failures":3,"path":"/health/live","period":"10s","port":8888,"timeout":"5s"},"memory":"1Gi","name":"hindsight-api","placement":{"eligibleNodes":["enschede-t1000-1","enschede-rx7900xtx-1","enschede-gtx-960m-1","enschede-pi-1","enschede-pi-2","enschede-pi-3"]},"readiness":{"failures":3,"path":"/health","period":"10s","port":8888,"timeout":"5s"},"replicas":1,"runtime":"python","secrets":[{"access":"read","delivery":"env","destination":"hindsight-api-platform-hindsight","keys":["llm-api-key"],"path":"secret/data/platform/hindsight","restartTargets":["hindsight-api"]},{"delivery":"env","destination":"hindsight-api-database-creds-knowledge-platform-owner","engine":"database","paths":[{"allows":["read"],"path":"database/creds/knowledge-platform-owner"}],"restartTargets":["hindsight-api"]}],"startup":{"failures":24,"path":"/health/live","period":"5s","port":8888,"timeout":"5s"},"surfaces":[{"name":"api","port":8888}],"switchover":"blue-green","uid":1000},{"consumes":["platform-postgres.postgres","hindsight-embedding.embeddings"],"cpu":"500m","cutover":"continuous","deadline":"480s","dependencies":[{"address":"platform-postgres:5432","application":"platform-postgres","peers":[{"namespace":"data-system","port":5432,"process":"postgres"}],"surface":"postgres","target":"postgres.data-system.svc.cluster.local"},{"address":"hindsight-tei-embedding:8091","application":"hindsight-embedding","peers":[{"namespace":"knowledge-platform-system","port":8091,"process":"hindsight-tei-embedding"}],"surface":"embeddings"}],"egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"},{"namespace":"secrets-system","port":8200,"process":"vault","rule":"secret-store"}],"environment":[{"name":"DEPLOYMENT_ENVIRONMENT","value":"production"},{"name":"HINDSIGHT_API_EMBEDDINGS_PROVIDER","value":"tei"},{"name":"HINDSIGHT_API_LLM_API_KEY","secret":{"key":"llm-api-key","path":"secret/data/platform/hindsight"}},{"name":"HINDSIGHT_API_LLM_BASE_URL","value":"https://eu.openrouter.ai/api/v1"},{"name":"HINDSIGHT_API_LLM_MODEL","value":"mistralai/mistral-small-2603"},{"name":"HINDSIGHT_API_LLM_PROVIDER","value":"openai"},{"name":"HINDSIGHT_API_PORT","value":"8889"},{"name":"HINDSIGHT_API_WORKER_BATCH_SIZE","value":"10"},{"name":"HINDSIGHT_API_WORKER_HTTP_PORT","value":"8889"},{"name":"HINDSIGHT_API_WORKER_MAX_RETRIES","value":"3"},{"name":"HINDSIGHT_API_WORKER_POLL_INTERVAL_MS","value":"500"},{"name":"OTEL_EXPORTER_OTLP_ENDPOINT","value":"http://otel-collector.observability-system.svc.cluster.local:4317"},{"name":"OTEL_SERVICE_NAME","value":"hindsight-worker"},{"name":"PORT","value":"8889"}],"gid":1000,"hardening":"restricted","identity":"hindsight-worker","identityToken":false,"image":"ghcr.io/vectorize-io/hindsight-api@sha256:7b14a1f4062252992d0176758753615e0a2071d9a269995be007be223ab01812","imageVersion":"0.4.0","ingress":[{"consumes":"hindsight.worker","port":8889,"rule":"consumer"}],"lifecycle":"application","liveness":{"failures":3,"path":"/health/live","period":"10s","port":8889,"timeout":"5s"},"memory":"1Gi","name":"hindsight-worker","parked":true,"placement":{"eligibleNodes":["enschede-t1000-1","enschede-rx7900xtx-1","enschede-gtx-960m-1","enschede-pi-1","enschede-pi-2","enschede-pi-3"]},"readiness":{"failures":3,"path":"/health","period":"10s","port":8889,"timeout":"5s"},"replicas":0,"runtime":"python","secrets":[{"access":"read","delivery":"env","destination":"hindsight-worker-platform-hindsight","keys":["llm-api-key"],"path":"secret/data/platform/hindsight","restartTargets":["hindsight-worker"]},{"delivery":"file","destination":"hindsight-worker-database-creds-knowledge-platform-owner","engine":"database","fileMode":"0400","mountAt":"/run/secrets/database","paths":[{"allows":["read"],"path":"database/creds/knowledge-platform-owner"}],"restartTargets":["hindsight-worker"]}],"startup":{"failures":24,"path":"/health/live","period":"5s","port":8889,"timeout":"5s"},"surfaces":[{"name":"worker","port":8889}],"switchover":"stop-start","uid":1000},{"consumes":["hindsight.api"],"cpu":"250m","cutover":"continuous","deadline":"300s","dependencies":[{"address":"hindsight-api:8888","application":"hindsight","peers":[{"namespace":"knowledge-platform-system","port":8888,"process":"hindsight-api"}],"surface":"api"}],"egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"}],"environment":[{"name":"DEPLOYMENT_ENVIRONMENT","value":"production"},{"name":"HINDSIGHT_CP_HOSTNAME","value":"0.0.0.0"},{"name":"HINDSIGHT_CP_PORT","value":"3000"},{"name":"NODE_ENV","value":"production"},{"name":"OTEL_EXPORTER_OTLP_ENDPOINT","value":"http://otel-collector.observability-system.svc.cluster.local:4317"},{"name":"OTEL_SERVICE_NAME","value":"hindsight-control-plane"},{"name":"PORT","value":"3000"}],"gid":1000,"hardening":"restricted","identity":"hindsight-control-plane","identityToken":false,"image":"ghcr.io/vectorize-io/hindsight-control-plane@sha256:d9bf57083f7cb0a53ee5011bdfde6aae2490dbf5c360d2d1bff10473b45eb112","imageVersion":"0.4.0","ingress":[{"namespace":"edge-system","port":3000,"process":"traefik-public","rule":"tier-proxy"},{"consumes":"hindsight.console","port":3000,"rule":"consumer"},{"namespace":"delivery-system","port":3000,"process":"release-gate","rule":"release-gate"}],"lifecycle":"application","liveness":{"failures":3,"period":"10s","tcp":3000,"timeout":"5s"},"memory":"512Mi","name":"hindsight-control-plane","placement":{"eligibleNodes":["enschede-t1000-1","enschede-rx7900xtx-1","enschede-gtx-960m-1","enschede-pi-1","enschede-pi-2","enschede-pi-3"]},"readiness":{"failures":3,"period":"10s","tcp":3000,"timeout":"5s"},"replicas":1,"runtime":"node","startup":{"failures":12,"period":"5s","tcp":3000,"timeout":"5s"},"surfaces":[{"name":"console","port":3000}],"switchover":"blue-green","uid":1000}],"project":"knowledge-platform","provenance":{"inputDigests":[{"digest":"sha256:5391c90578e87c9e5a3697dd7f32268e92aa578786ba33a2f82ebe50b392a42e","input":"intent-fragment","name":"data"},{"digest":"sha256:ebb5134deeb55f320e28609054a0aca85a359f3869501a3a8b7560d7d05a4277","input":"intent-fragment","name":"delivery"},{"digest":"sha256:9a29fae42c0c6cd4f9824d7af3d9ecc68c068a3a00d1f9488bfbb70c882ea565","input":"intent-fragment","name":"edge"},{"digest":"sha256:c45963dcb3911475bff7d4fd17c0ae570230bdadba4ff2a68c75272d9219e762","input":"intent-fragment","name":"identity"},{"digest":"sha256:50542ac63f880a0818a38f350b0cf5e0b3cd6ced6021f85ce98b9d1fdde63f27","input":"intent-fragment","name":"knowledge-platform"},{"digest":"sha256:447e1500b09b608cecc0f93ebb598a60e655690822ecc63395e4e40eaadb98cb","input":"intent-fragment","name":"mail"},{"digest":"sha256:5916815b3b70a0de96137a6614f9356003985016bbf078bafe098433c9aab7d1","input":"intent-fragment","name":"observability"},{"digest":"sha256:31309996835674cc4f037f8e19897ddbe4a7fa8a487a3efc613e251caeed0853","input":"intent-fragment","name":"secrets"},{"digest":"sha256:f7e7e1e0a33d9528e11d956c36b5a6b8f37f758fb98aecdd74ea3eaf7286d60e","input":"platform-intent","name":"jorisjonkers.dev"},{"digest":"sha256:a989e80337efb294fa1a7175b3ad3eea8bed9bf13fbd503a26691c9265575fc2","input":"node-contract","name":"production"},{"digest":"sha256:f09f02097230414af40101e8ebe053e62d679c085a7d1fed9d860c8ba151b68b","input":"images-lock","name":"estate"},{"digest":"sha256:202e5e2a22ab20cfe5b6e85cbd8139cb13ed3b199b2f1af4380068d21d54ff1f","input":"cluster-state","name":"production"}],"renderHash":"sha256:817217a81d3ab49b3381bc31ebad23f0258563587ca65ed4a3dd41ae99665346","schemaPackageIntegrity":"sha256:5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b"},"reconcileAfter":["apps-data","apps-edge","apps-secrets"],"reconcileUnit":"apps-knowledge-platform","releaseGate":{"analysis":{"errorRate":"1%","interval":"30s","iterations":4,"latency":"500ms","threshold":3},"deadline":"480s","endpoint":"http://release-gate.delivery-system.svc.cluster.local:8080","members":[{"checks":["error-rate","latency"],"process":"hindsight-api","readiness":{"path":"/health","port":8888},"waitsFor":[{"application":"hindsight-embedding","process":"hindsight-tei-embedding"}]},{"checks":["error-rate","latency"],"process":"hindsight-control-plane","readiness":{"tcp":3000}}]},"revision":"sha256:70f6c35b565efcfb54d51445c337dba88caa6cba6664a0c1ae58bb651534a116","secretStore":"http://vault.secrets-system.svc.cluster.local:8200"} \ No newline at end of file diff --git a/spec/v1/examples/knowledge-platform/rendered/apps/knowledge-platform/hindsight/canary.yaml b/spec/v1/examples/knowledge-platform/rendered/apps/knowledge-platform/hindsight/canary.yaml index 1845dd667..a16d96176 100644 --- a/spec/v1/examples/knowledge-platform/rendered/apps/knowledge-platform/hindsight/canary.yaml +++ b/spec/v1/examples/knowledge-platform/rendered/apps/knowledge-platform/hindsight/canary.yaml @@ -33,7 +33,7 @@ spec: metadata: application: hindsight process: hindsight-api - revision: sha256:932b57848c01da8ff277900e56678d27f4f8199d36c5e1a68f59012b6a4a5de6 + revision: sha256:70f6c35b565efcfb54d51445c337dba88caa6cba6664a0c1ae58bb651534a116 paths: / readiness: /health hostnames: memory-api.jorisjonkers.dev @@ -45,7 +45,7 @@ spec: metadata: application: hindsight process: hindsight-api - revision: sha256:932b57848c01da8ff277900e56678d27f4f8199d36c5e1a68f59012b6a4a5de6 + revision: sha256:70f6c35b565efcfb54d51445c337dba88caa6cba6664a0c1ae58bb651534a116 paths: / readiness: /health hostnames: memory-api.jorisjonkers.dev @@ -57,7 +57,7 @@ spec: metadata: application: hindsight process: hindsight-api - revision: sha256:932b57848c01da8ff277900e56678d27f4f8199d36c5e1a68f59012b6a4a5de6 + revision: sha256:70f6c35b565efcfb54d51445c337dba88caa6cba6664a0c1ae58bb651534a116 paths: / readiness: /health hostnames: memory-api.jorisjonkers.dev @@ -97,7 +97,7 @@ spec: metadata: application: hindsight process: hindsight-control-plane - revision: sha256:932b57848c01da8ff277900e56678d27f4f8199d36c5e1a68f59012b6a4a5de6 + revision: sha256:70f6c35b565efcfb54d51445c337dba88caa6cba6664a0c1ae58bb651534a116 paths: / hostnames: memory.jorisjonkers.dev errorRate: 1% @@ -108,7 +108,7 @@ spec: metadata: application: hindsight process: hindsight-control-plane - revision: sha256:932b57848c01da8ff277900e56678d27f4f8199d36c5e1a68f59012b6a4a5de6 + revision: sha256:70f6c35b565efcfb54d51445c337dba88caa6cba6664a0c1ae58bb651534a116 paths: / hostnames: memory.jorisjonkers.dev errorRate: 1% @@ -119,7 +119,7 @@ spec: metadata: application: hindsight process: hindsight-control-plane - revision: sha256:932b57848c01da8ff277900e56678d27f4f8199d36c5e1a68f59012b6a4a5de6 + revision: sha256:70f6c35b565efcfb54d51445c337dba88caa6cba6664a0c1ae58bb651534a116 paths: / hostnames: memory.jorisjonkers.dev errorRate: 1% diff --git a/spec/v1/examples/knowledge-platform/rendered/apps/knowledge-platform/hindsight/networkpolicy.yaml b/spec/v1/examples/knowledge-platform/rendered/apps/knowledge-platform/hindsight/networkpolicy.yaml index 64c928be9..cfef73022 100644 --- a/spec/v1/examples/knowledge-platform/rendered/apps/knowledge-platform/hindsight/networkpolicy.yaml +++ b/spec/v1/examples/knowledge-platform/rendered/apps/knowledge-platform/hindsight/networkpolicy.yaml @@ -39,6 +39,16 @@ spec: ports: - protocol: TCP port: 8888 + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: delivery-system + podSelector: + matchLabels: + app.kubernetes.io/instance: release-gate + ports: + - protocol: TCP + port: 8888 egress: - to: - namespaceSelector: @@ -193,6 +203,16 @@ spec: ports: - protocol: TCP port: 3000 + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: delivery-system + podSelector: + matchLabels: + app.kubernetes.io/instance: release-gate + ports: + - protocol: TCP + port: 3000 egress: - to: - namespaceSelector: diff --git a/spec/v1/examples/knowledge-platform/rendered/apps/knowledge-platform/hindsight/vso.yaml b/spec/v1/examples/knowledge-platform/rendered/apps/knowledge-platform/hindsight/vso.yaml index 471f9ff20..8db7bd9c7 100644 --- a/spec/v1/examples/knowledge-platform/rendered/apps/knowledge-platform/hindsight/vso.yaml +++ b/spec/v1/examples/knowledge-platform/rendered/apps/knowledge-platform/hindsight/vso.yaml @@ -12,7 +12,7 @@ metadata: app.kubernetes.io/managed-by: deploy-kit app.kubernetes.io/component: python annotations: - estate.jorisjonkers.dev/revision: sha256:932b57848c01da8ff277900e56678d27f4f8199d36c5e1a68f59012b6a4a5de6 + estate.jorisjonkers.dev/revision: sha256:70f6c35b565efcfb54d51445c337dba88caa6cba6664a0c1ae58bb651534a116 kustomize.toolkit.fluxcd.io/prune: disabled spec: vaultConnectionRef: secret-store @@ -34,7 +34,7 @@ metadata: app.kubernetes.io/managed-by: deploy-kit app.kubernetes.io/component: python annotations: - estate.jorisjonkers.dev/revision: sha256:932b57848c01da8ff277900e56678d27f4f8199d36c5e1a68f59012b6a4a5de6 + estate.jorisjonkers.dev/revision: sha256:70f6c35b565efcfb54d51445c337dba88caa6cba6664a0c1ae58bb651534a116 kustomize.toolkit.fluxcd.io/prune: disabled spec: vaultAuthRef: hindsight-api @@ -64,7 +64,7 @@ metadata: app.kubernetes.io/managed-by: deploy-kit app.kubernetes.io/component: python annotations: - estate.jorisjonkers.dev/revision: sha256:932b57848c01da8ff277900e56678d27f4f8199d36c5e1a68f59012b6a4a5de6 + estate.jorisjonkers.dev/revision: sha256:70f6c35b565efcfb54d51445c337dba88caa6cba6664a0c1ae58bb651534a116 kustomize.toolkit.fluxcd.io/prune: disabled spec: vaultAuthRef: hindsight-api @@ -91,7 +91,7 @@ metadata: app.kubernetes.io/managed-by: deploy-kit app.kubernetes.io/component: python annotations: - estate.jorisjonkers.dev/revision: sha256:932b57848c01da8ff277900e56678d27f4f8199d36c5e1a68f59012b6a4a5de6 + estate.jorisjonkers.dev/revision: sha256:70f6c35b565efcfb54d51445c337dba88caa6cba6664a0c1ae58bb651534a116 kustomize.toolkit.fluxcd.io/prune: disabled spec: vaultConnectionRef: secret-store @@ -113,7 +113,7 @@ metadata: app.kubernetes.io/managed-by: deploy-kit app.kubernetes.io/component: python annotations: - estate.jorisjonkers.dev/revision: sha256:932b57848c01da8ff277900e56678d27f4f8199d36c5e1a68f59012b6a4a5de6 + estate.jorisjonkers.dev/revision: sha256:70f6c35b565efcfb54d51445c337dba88caa6cba6664a0c1ae58bb651534a116 kustomize.toolkit.fluxcd.io/prune: disabled spec: vaultAuthRef: hindsight-worker @@ -143,7 +143,7 @@ metadata: app.kubernetes.io/managed-by: deploy-kit app.kubernetes.io/component: python annotations: - estate.jorisjonkers.dev/revision: sha256:932b57848c01da8ff277900e56678d27f4f8199d36c5e1a68f59012b6a4a5de6 + estate.jorisjonkers.dev/revision: sha256:70f6c35b565efcfb54d51445c337dba88caa6cba6664a0c1ae58bb651534a116 kustomize.toolkit.fluxcd.io/prune: disabled spec: vaultAuthRef: hindsight-worker diff --git a/spec/v1/examples/knowledge-platform/rendered/apps/knowledge-platform/hindsight/workload.yaml b/spec/v1/examples/knowledge-platform/rendered/apps/knowledge-platform/hindsight/workload.yaml index 91eb1ba88..7072b8531 100644 --- a/spec/v1/examples/knowledge-platform/rendered/apps/knowledge-platform/hindsight/workload.yaml +++ b/spec/v1/examples/knowledge-platform/rendered/apps/knowledge-platform/hindsight/workload.yaml @@ -32,6 +32,7 @@ spec: app.kubernetes.io/component: python consumes.deploy-kit.jorisjonkers.dev/platform-postgres.postgres: "true" consumes.deploy-kit.jorisjonkers.dev/hindsight-embedding.embeddings: "true" + gated.deploy-kit.jorisjonkers.dev/release-gate: "true" annotations: image.jorisjonkers.dev/hindsight-api: 0.4.0 spec: @@ -309,6 +310,7 @@ spec: app.kubernetes.io/managed-by: deploy-kit app.kubernetes.io/component: node consumes.deploy-kit.jorisjonkers.dev/hindsight.api: "true" + gated.deploy-kit.jorisjonkers.dev/release-gate: "true" annotations: image.jorisjonkers.dev/hindsight-control-plane: 0.4.0 spec: diff --git a/spec/v1/examples/minimal/expected/resolved.json b/spec/v1/examples/minimal/expected/resolved.json index ae3653342..b3b84026f 100644 --- a/spec/v1/examples/minimal/expected/resolved.json +++ b/spec/v1/examples/minimal/expected/resolved.json @@ -1 +1 @@ -{"alertClass":"business-hours","apiVersion":"resolved.jorisjonkers.dev/v1","exposure":[{"certificates":"acme","dns":{"proxied":true,"target":"ingress.jorisjonkers.dev"},"host":"notes.jorisjonkers.dev","ingressClass":"traefik-public","listener":"tls","name":"public","proxy":{"application":"traefik-public","namespace":"edge-system"},"routes":[{"audience":"anonymous","match":"prefix","middleware":[{"contentPolicy":"strict","kind":"security-headers"}],"path":"/","precedence":1,"process":"notes-api","surface":"http"}],"tier":"public-frankfurt"}],"id":"notes","kind":"ResolvedApplication","namespace":"notes-system","processes":[{"cpu":"50m","cutover":"continuous","deadline":"180s","egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"}],"environment":[{"name":"DEPLOYMENT_ENVIRONMENT","value":"production"},{"name":"NODE_ENV","value":"production"},{"name":"NOTES_PAGE_SIZE","value":"50"},{"name":"OTEL_EXPORTER_OTLP_ENDPOINT","value":"http://otel-collector.observability-system.svc.cluster.local:4317"},{"name":"OTEL_SERVICE_NAME","value":"notes-api"},{"name":"PORT","value":"8080"}],"gid":1000,"gpu":{"resource":"nvidia.com/gpu","runtimeClass":"nvidia-cdi"},"hardening":"restricted","identity":"notes-api","identityToken":false,"image":"ghcr.io/jorisjonkers-dev/notes/notes-api@sha256:9c1e5a4b7d2f8e3a6b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a","imageVersion":"1.4.0","ingress":[{"namespace":"edge-system","port":8080,"process":"traefik-public","rule":"tier-proxy"},{"namespace":"observability-system","port":8080,"process":"prometheus","rule":"metrics-stack"},{"consumes":"notes.http","port":8080,"rule":"consumer"}],"libraries":[{"access":"write","claim":"library-media","gid":3001,"library":"media","mountAt":"/attachments","node":"enschede-t1000-1","path":"/srv/media","subPath":"Notes","volume":"notes-system-media"}],"lifecycle":"application","liveness":{"failures":3,"path":"/healthz/live","period":"10s","port":8080,"timeout":"5s"},"memory":"256Mi","name":"notes-api","placement":{"eligibleNodes":["enschede-t1000-1"]},"readiness":{"failures":3,"path":"/healthz/ready","period":"10s","port":8080,"timeout":"5s"},"replicas":1,"runtime":"node","startup":{"failures":4,"path":"/healthz/live","period":"5s","port":8080,"timeout":"5s"},"surfaces":[{"name":"http","port":8080}],"switchover":"blue-green","uid":1000}],"project":"notes","provenance":{"inputDigests":[{"digest":"sha256:ebb5134deeb55f320e28609054a0aca85a359f3869501a3a8b7560d7d05a4277","input":"intent-fragment","name":"delivery"},{"digest":"sha256:9a29fae42c0c6cd4f9824d7af3d9ecc68c068a3a00d1f9488bfbb70c882ea565","input":"intent-fragment","name":"edge"},{"digest":"sha256:c45963dcb3911475bff7d4fd17c0ae570230bdadba4ff2a68c75272d9219e762","input":"intent-fragment","name":"identity"},{"digest":"sha256:447e1500b09b608cecc0f93ebb598a60e655690822ecc63395e4e40eaadb98cb","input":"intent-fragment","name":"mail"},{"digest":"sha256:35d5e3643093422537ea72def0f4b19e93ed319a2d0192fc7846342970ce8797","input":"intent-fragment","name":"notes"},{"digest":"sha256:5916815b3b70a0de96137a6614f9356003985016bbf078bafe098433c9aab7d1","input":"intent-fragment","name":"observability"},{"digest":"sha256:31309996835674cc4f037f8e19897ddbe4a7fa8a487a3efc613e251caeed0853","input":"intent-fragment","name":"secrets"},{"digest":"sha256:f7e7e1e0a33d9528e11d956c36b5a6b8f37f758fb98aecdd74ea3eaf7286d60e","input":"platform-intent","name":"jorisjonkers.dev"},{"digest":"sha256:a989e80337efb294fa1a7175b3ad3eea8bed9bf13fbd503a26691c9265575fc2","input":"node-contract","name":"production"},{"digest":"sha256:f09f02097230414af40101e8ebe053e62d679c085a7d1fed9d860c8ba151b68b","input":"images-lock","name":"estate"},{"digest":"sha256:202e5e2a22ab20cfe5b6e85cbd8139cb13ed3b199b2f1af4380068d21d54ff1f","input":"cluster-state","name":"production"}],"renderHash":"sha256:c944fc023ec1bda7d6df2150acd36374ae0d7f8aae46e54baf8c2ec48f1e2f1a","schemaPackageIntegrity":"sha256:5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b"},"reconcileAfter":["apps-edge"],"reconcileUnit":"apps-notes","releaseGate":{"analysis":{"errorRate":"1%","interval":"30s","iterations":4,"latency":"500ms","threshold":3},"deadline":"180s","endpoint":"http://release-gate.delivery-system.svc.cluster.local:8080","members":[{"checks":["error-rate","latency"],"process":"notes-api","readiness":{"path":"/healthz/ready","port":8080}}]},"revision":"sha256:72cf80f9e8a4e1c12dd833e424aa7c9d5396d184a7c1460666728061b82dc555","scrape":{"interval":"30s","labels":{"release":"metrics-stack"},"path":"/metrics","process":"notes-api","surface":"http","timeout":"10s"}} \ No newline at end of file +{"alertClass":"business-hours","apiVersion":"resolved.jorisjonkers.dev/v1","exposure":[{"certificates":"acme","dns":{"proxied":true,"target":"ingress.jorisjonkers.dev"},"host":"notes.jorisjonkers.dev","ingressClass":"traefik-public","listener":"tls","name":"public","proxy":{"application":"traefik-public","namespace":"edge-system"},"routes":[{"audience":"anonymous","match":"prefix","middleware":[{"contentPolicy":"strict","kind":"security-headers"}],"path":"/","precedence":1,"process":"notes-api","surface":"http"}],"tier":"public-frankfurt"}],"id":"notes","kind":"ResolvedApplication","namespace":"notes-system","processes":[{"cpu":"50m","cutover":"continuous","deadline":"180s","egress":[{"namespace":"kube-system","port":53,"rule":"cluster-dns"}],"environment":[{"name":"DEPLOYMENT_ENVIRONMENT","value":"production"},{"name":"NODE_ENV","value":"production"},{"name":"NOTES_PAGE_SIZE","value":"50"},{"name":"OTEL_EXPORTER_OTLP_ENDPOINT","value":"http://otel-collector.observability-system.svc.cluster.local:4317"},{"name":"OTEL_SERVICE_NAME","value":"notes-api"},{"name":"PORT","value":"8080"}],"gid":1000,"gpu":{"resource":"nvidia.com/gpu","runtimeClass":"nvidia-cdi"},"hardening":"restricted","identity":"notes-api","identityToken":false,"image":"ghcr.io/jorisjonkers-dev/notes/notes-api@sha256:9c1e5a4b7d2f8e3a6b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a","imageVersion":"1.4.0","ingress":[{"namespace":"edge-system","port":8080,"process":"traefik-public","rule":"tier-proxy"},{"namespace":"observability-system","port":8080,"process":"prometheus","rule":"metrics-stack"},{"consumes":"notes.http","port":8080,"rule":"consumer"},{"namespace":"delivery-system","port":8080,"process":"release-gate","rule":"release-gate"}],"libraries":[{"access":"write","claim":"library-media","gid":3001,"library":"media","mountAt":"/attachments","node":"enschede-t1000-1","path":"/srv/media","subPath":"Notes","volume":"notes-system-media"}],"lifecycle":"application","liveness":{"failures":3,"path":"/healthz/live","period":"10s","port":8080,"timeout":"5s"},"memory":"256Mi","name":"notes-api","placement":{"eligibleNodes":["enschede-t1000-1"]},"readiness":{"failures":3,"path":"/healthz/ready","period":"10s","port":8080,"timeout":"5s"},"replicas":1,"runtime":"node","startup":{"failures":4,"path":"/healthz/live","period":"5s","port":8080,"timeout":"5s"},"surfaces":[{"name":"http","port":8080}],"switchover":"blue-green","uid":1000}],"project":"notes","provenance":{"inputDigests":[{"digest":"sha256:ebb5134deeb55f320e28609054a0aca85a359f3869501a3a8b7560d7d05a4277","input":"intent-fragment","name":"delivery"},{"digest":"sha256:9a29fae42c0c6cd4f9824d7af3d9ecc68c068a3a00d1f9488bfbb70c882ea565","input":"intent-fragment","name":"edge"},{"digest":"sha256:c45963dcb3911475bff7d4fd17c0ae570230bdadba4ff2a68c75272d9219e762","input":"intent-fragment","name":"identity"},{"digest":"sha256:447e1500b09b608cecc0f93ebb598a60e655690822ecc63395e4e40eaadb98cb","input":"intent-fragment","name":"mail"},{"digest":"sha256:35d5e3643093422537ea72def0f4b19e93ed319a2d0192fc7846342970ce8797","input":"intent-fragment","name":"notes"},{"digest":"sha256:5916815b3b70a0de96137a6614f9356003985016bbf078bafe098433c9aab7d1","input":"intent-fragment","name":"observability"},{"digest":"sha256:31309996835674cc4f037f8e19897ddbe4a7fa8a487a3efc613e251caeed0853","input":"intent-fragment","name":"secrets"},{"digest":"sha256:f7e7e1e0a33d9528e11d956c36b5a6b8f37f758fb98aecdd74ea3eaf7286d60e","input":"platform-intent","name":"jorisjonkers.dev"},{"digest":"sha256:a989e80337efb294fa1a7175b3ad3eea8bed9bf13fbd503a26691c9265575fc2","input":"node-contract","name":"production"},{"digest":"sha256:f09f02097230414af40101e8ebe053e62d679c085a7d1fed9d860c8ba151b68b","input":"images-lock","name":"estate"},{"digest":"sha256:202e5e2a22ab20cfe5b6e85cbd8139cb13ed3b199b2f1af4380068d21d54ff1f","input":"cluster-state","name":"production"}],"renderHash":"sha256:c944fc023ec1bda7d6df2150acd36374ae0d7f8aae46e54baf8c2ec48f1e2f1a","schemaPackageIntegrity":"sha256:5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b5e6f7a8b"},"reconcileAfter":["apps-edge"],"reconcileUnit":"apps-notes","releaseGate":{"analysis":{"errorRate":"1%","interval":"30s","iterations":4,"latency":"500ms","threshold":3},"deadline":"180s","endpoint":"http://release-gate.delivery-system.svc.cluster.local:8080","members":[{"checks":["error-rate","latency"],"process":"notes-api","readiness":{"path":"/healthz/ready","port":8080}}]},"revision":"sha256:1286f40637d2d10a95d3ff5955a5ed51328c334b6928442d492f9ab0f54fb03d","scrape":{"interval":"30s","labels":{"release":"metrics-stack"},"path":"/metrics","process":"notes-api","surface":"http","timeout":"10s"}} \ No newline at end of file diff --git a/spec/v1/examples/minimal/rendered/apps/notes/notes/canary.yaml b/spec/v1/examples/minimal/rendered/apps/notes/notes/canary.yaml index b9d207732..e920d0429 100644 --- a/spec/v1/examples/minimal/rendered/apps/notes/notes/canary.yaml +++ b/spec/v1/examples/minimal/rendered/apps/notes/notes/canary.yaml @@ -33,7 +33,7 @@ spec: metadata: application: notes process: notes-api - revision: sha256:72cf80f9e8a4e1c12dd833e424aa7c9d5396d184a7c1460666728061b82dc555 + revision: sha256:1286f40637d2d10a95d3ff5955a5ed51328c334b6928442d492f9ab0f54fb03d paths: / readiness: /healthz/ready hostnames: notes.jorisjonkers.dev @@ -45,7 +45,7 @@ spec: metadata: application: notes process: notes-api - revision: sha256:72cf80f9e8a4e1c12dd833e424aa7c9d5396d184a7c1460666728061b82dc555 + revision: sha256:1286f40637d2d10a95d3ff5955a5ed51328c334b6928442d492f9ab0f54fb03d paths: / readiness: /healthz/ready hostnames: notes.jorisjonkers.dev @@ -57,7 +57,7 @@ spec: metadata: application: notes process: notes-api - revision: sha256:72cf80f9e8a4e1c12dd833e424aa7c9d5396d184a7c1460666728061b82dc555 + revision: sha256:1286f40637d2d10a95d3ff5955a5ed51328c334b6928442d492f9ab0f54fb03d paths: / readiness: /healthz/ready hostnames: notes.jorisjonkers.dev diff --git a/spec/v1/examples/minimal/rendered/apps/notes/notes/networkpolicy.yaml b/spec/v1/examples/minimal/rendered/apps/notes/notes/networkpolicy.yaml index 022173354..a3d5d3020 100644 --- a/spec/v1/examples/minimal/rendered/apps/notes/notes/networkpolicy.yaml +++ b/spec/v1/examples/minimal/rendered/apps/notes/notes/networkpolicy.yaml @@ -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: diff --git a/spec/v1/examples/minimal/rendered/apps/notes/notes/workload.yaml b/spec/v1/examples/minimal/rendered/apps/notes/notes/workload.yaml index d80383f80..44927babf 100644 --- a/spec/v1/examples/minimal/rendered/apps/notes/notes/workload.yaml +++ b/spec/v1/examples/minimal/rendered/apps/notes/notes/workload.yaml @@ -30,6 +30,7 @@ spec: app.kubernetes.io/part-of: notes app.kubernetes.io/managed-by: deploy-kit app.kubernetes.io/component: node + gated.deploy-kit.jorisjonkers.dev/release-gate: "true" annotations: image.jorisjonkers.dev/notes-api: 1.4.0 spec: diff --git a/spec/v1/examples/observability/rendered/apps/observability/alertmanager/canary.yaml b/spec/v1/examples/observability/rendered/apps/observability/alertmanager/canary.yaml index 4378da9c5..011df3ad7 100644 --- a/spec/v1/examples/observability/rendered/apps/observability/alertmanager/canary.yaml +++ b/spec/v1/examples/observability/rendered/apps/observability/alertmanager/canary.yaml @@ -33,7 +33,7 @@ spec: metadata: application: alertmanager process: alertmanager - revision: sha256:cd18d9280f84cf0a015bff0cdbd9f5cb56089945057811df6b9d2d19593780b2 + revision: sha256:d9f557e6af7f39728e3985f209dbcdb62583cae345612e85f262db489819262f readiness: /-/ready errorRate: 1% latency: 500ms @@ -43,7 +43,7 @@ spec: metadata: application: alertmanager process: alertmanager - revision: sha256:cd18d9280f84cf0a015bff0cdbd9f5cb56089945057811df6b9d2d19593780b2 + revision: sha256:d9f557e6af7f39728e3985f209dbcdb62583cae345612e85f262db489819262f readiness: /-/ready errorRate: 1% latency: 500ms @@ -53,7 +53,7 @@ spec: metadata: application: alertmanager process: alertmanager - revision: sha256:cd18d9280f84cf0a015bff0cdbd9f5cb56089945057811df6b9d2d19593780b2 + revision: sha256:d9f557e6af7f39728e3985f209dbcdb62583cae345612e85f262db489819262f readiness: /-/ready errorRate: 1% latency: 500ms diff --git a/spec/v1/examples/observability/rendered/apps/observability/alertmanager/networkpolicy.yaml b/spec/v1/examples/observability/rendered/apps/observability/alertmanager/networkpolicy.yaml index 825f5496d..1be1bec90 100644 --- a/spec/v1/examples/observability/rendered/apps/observability/alertmanager/networkpolicy.yaml +++ b/spec/v1/examples/observability/rendered/apps/observability/alertmanager/networkpolicy.yaml @@ -29,6 +29,16 @@ spec: ports: - protocol: TCP port: 9093 + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: delivery-system + podSelector: + matchLabels: + app.kubernetes.io/instance: release-gate + ports: + - protocol: TCP + port: 9093 egress: - to: - namespaceSelector: diff --git a/spec/v1/examples/observability/rendered/apps/observability/alertmanager/vso.yaml b/spec/v1/examples/observability/rendered/apps/observability/alertmanager/vso.yaml index dd3f6e35a..c5e1810db 100644 --- a/spec/v1/examples/observability/rendered/apps/observability/alertmanager/vso.yaml +++ b/spec/v1/examples/observability/rendered/apps/observability/alertmanager/vso.yaml @@ -12,7 +12,7 @@ metadata: app.kubernetes.io/managed-by: deploy-kit app.kubernetes.io/component: none annotations: - estate.jorisjonkers.dev/revision: sha256:cd18d9280f84cf0a015bff0cdbd9f5cb56089945057811df6b9d2d19593780b2 + estate.jorisjonkers.dev/revision: sha256:d9f557e6af7f39728e3985f209dbcdb62583cae345612e85f262db489819262f kustomize.toolkit.fluxcd.io/prune: disabled spec: vaultConnectionRef: secret-store @@ -34,7 +34,7 @@ metadata: app.kubernetes.io/managed-by: deploy-kit app.kubernetes.io/component: none annotations: - estate.jorisjonkers.dev/revision: sha256:cd18d9280f84cf0a015bff0cdbd9f5cb56089945057811df6b9d2d19593780b2 + estate.jorisjonkers.dev/revision: sha256:d9f557e6af7f39728e3985f209dbcdb62583cae345612e85f262db489819262f kustomize.toolkit.fluxcd.io/prune: disabled spec: vaultAuthRef: alertmanager diff --git a/spec/v1/examples/observability/rendered/apps/observability/alertmanager/workload.yaml b/spec/v1/examples/observability/rendered/apps/observability/alertmanager/workload.yaml index a25cb6133..e590e0ea0 100644 --- a/spec/v1/examples/observability/rendered/apps/observability/alertmanager/workload.yaml +++ b/spec/v1/examples/observability/rendered/apps/observability/alertmanager/workload.yaml @@ -30,6 +30,7 @@ spec: app.kubernetes.io/part-of: alertmanager app.kubernetes.io/managed-by: deploy-kit app.kubernetes.io/component: none + gated.deploy-kit.jorisjonkers.dev/release-gate: "true" annotations: image.jorisjonkers.dev/alertmanager: v0.28.1 spec: diff --git a/spec/v1/examples/observability/rendered/apps/observability/otel-collector/canary.yaml b/spec/v1/examples/observability/rendered/apps/observability/otel-collector/canary.yaml index 95718d893..020512f15 100644 --- a/spec/v1/examples/observability/rendered/apps/observability/otel-collector/canary.yaml +++ b/spec/v1/examples/observability/rendered/apps/observability/otel-collector/canary.yaml @@ -33,7 +33,7 @@ spec: metadata: application: otel-collector process: otel-collector - revision: sha256:828f117123cb12e30270c1d7acd09f21992b426e5af6629d6947ca678f8a4664 + revision: sha256:4bcdab7f158cf8d299aa2970116d870a1dd481e1a861082d99409a4d43d4fe29 readiness: / errorRate: 1% latency: 500ms @@ -43,7 +43,7 @@ spec: metadata: application: otel-collector process: otel-collector - revision: sha256:828f117123cb12e30270c1d7acd09f21992b426e5af6629d6947ca678f8a4664 + revision: sha256:4bcdab7f158cf8d299aa2970116d870a1dd481e1a861082d99409a4d43d4fe29 readiness: / errorRate: 1% latency: 500ms @@ -53,7 +53,7 @@ spec: metadata: application: otel-collector process: otel-collector - revision: sha256:828f117123cb12e30270c1d7acd09f21992b426e5af6629d6947ca678f8a4664 + revision: sha256:4bcdab7f158cf8d299aa2970116d870a1dd481e1a861082d99409a4d43d4fe29 readiness: / errorRate: 1% latency: 500ms diff --git a/spec/v1/examples/observability/rendered/apps/observability/otel-collector/networkpolicy.yaml b/spec/v1/examples/observability/rendered/apps/observability/otel-collector/networkpolicy.yaml index 46428baa8..a71195006 100644 --- a/spec/v1/examples/observability/rendered/apps/observability/otel-collector/networkpolicy.yaml +++ b/spec/v1/examples/observability/rendered/apps/observability/otel-collector/networkpolicy.yaml @@ -39,6 +39,16 @@ spec: ports: - protocol: TCP port: 13133 + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: delivery-system + podSelector: + matchLabels: + app.kubernetes.io/instance: release-gate + ports: + - protocol: TCP + port: 4317 egress: - to: - namespaceSelector: diff --git a/spec/v1/examples/observability/rendered/apps/observability/otel-collector/workload.yaml b/spec/v1/examples/observability/rendered/apps/observability/otel-collector/workload.yaml index a61c1f49e..b5db56ccd 100644 --- a/spec/v1/examples/observability/rendered/apps/observability/otel-collector/workload.yaml +++ b/spec/v1/examples/observability/rendered/apps/observability/otel-collector/workload.yaml @@ -30,6 +30,7 @@ spec: app.kubernetes.io/part-of: otel-collector app.kubernetes.io/managed-by: deploy-kit app.kubernetes.io/component: none + gated.deploy-kit.jorisjonkers.dev/release-gate: "true" annotations: image.jorisjonkers.dev/otel-collector: 0.120.0 spec: diff --git a/spec/v1/examples/observability/rendered/apps/observability/prometheus/canary.yaml b/spec/v1/examples/observability/rendered/apps/observability/prometheus/canary.yaml index 1043893c4..a03f7ef2c 100644 --- a/spec/v1/examples/observability/rendered/apps/observability/prometheus/canary.yaml +++ b/spec/v1/examples/observability/rendered/apps/observability/prometheus/canary.yaml @@ -33,7 +33,7 @@ spec: metadata: application: prometheus process: prometheus - revision: sha256:7c2a679eea6c8d8f9316f1a609120f0dcb99ea2e22843c2e48065e4804c9dae0 + revision: sha256:2e8a105e0d86dfd44e582952710d28ea2a651541e4e4334b348b18a40a81925b readiness: /-/ready errorRate: 1% latency: 500ms @@ -43,7 +43,7 @@ spec: metadata: application: prometheus process: prometheus - revision: sha256:7c2a679eea6c8d8f9316f1a609120f0dcb99ea2e22843c2e48065e4804c9dae0 + revision: sha256:2e8a105e0d86dfd44e582952710d28ea2a651541e4e4334b348b18a40a81925b readiness: /-/ready errorRate: 1% latency: 500ms @@ -53,7 +53,7 @@ spec: metadata: application: prometheus process: prometheus - revision: sha256:7c2a679eea6c8d8f9316f1a609120f0dcb99ea2e22843c2e48065e4804c9dae0 + revision: sha256:2e8a105e0d86dfd44e582952710d28ea2a651541e4e4334b348b18a40a81925b readiness: /-/ready errorRate: 1% latency: 500ms diff --git a/spec/v1/examples/observability/rendered/apps/observability/prometheus/networkpolicy.yaml b/spec/v1/examples/observability/rendered/apps/observability/prometheus/networkpolicy.yaml index 6c8d87f8a..9e0faa4f1 100644 --- a/spec/v1/examples/observability/rendered/apps/observability/prometheus/networkpolicy.yaml +++ b/spec/v1/examples/observability/rendered/apps/observability/prometheus/networkpolicy.yaml @@ -29,6 +29,16 @@ spec: ports: - protocol: TCP port: 9090 + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: delivery-system + podSelector: + matchLabels: + app.kubernetes.io/instance: release-gate + ports: + - protocol: TCP + port: 9090 egress: - to: - namespaceSelector: diff --git a/spec/v1/examples/observability/rendered/apps/observability/prometheus/workload.yaml b/spec/v1/examples/observability/rendered/apps/observability/prometheus/workload.yaml index ef33f503f..7c54ab956 100644 --- a/spec/v1/examples/observability/rendered/apps/observability/prometheus/workload.yaml +++ b/spec/v1/examples/observability/rendered/apps/observability/prometheus/workload.yaml @@ -30,6 +30,7 @@ spec: app.kubernetes.io/part-of: prometheus app.kubernetes.io/managed-by: deploy-kit app.kubernetes.io/component: none + gated.deploy-kit.jorisjonkers.dev/release-gate: "true" annotations: image.jorisjonkers.dev/prometheus: v3.2.1 spec: diff --git a/spec/v1/schemas/resolved-application.schema.json b/spec/v1/schemas/resolved-application.schema.json index 49f68d084..7f0a62c48 100644 --- a/spec/v1/schemas/resolved-application.schema.json +++ b/spec/v1/schemas/resolved-application.schema.json @@ -1403,7 +1403,14 @@ "egress": { "type": "array", "items": { - "$ref": "#/$defs/EgressPeer" + "anyOf": [ + { + "$ref": "#/$defs/EgressPeer" + }, + { + "$ref": "#/$defs/GateMembersPeer" + } + ] } } }, @@ -2666,8 +2673,22 @@ "tier-proxy", "metrics-stack", "backup", - "flux-events" + "flux-events", + "release-gate" ] + }, + "GateMembersPeer": { + "type": "object", + "properties": { + "rule": { + "type": "string", + "const": "gate-members" + } + }, + "required": [ + "rule" + ], + "additionalProperties": false } } } diff --git a/spec/v1/schemas/resolved-deployment.schema.json b/spec/v1/schemas/resolved-deployment.schema.json index 73ce49711..28c66a4e2 100644 --- a/spec/v1/schemas/resolved-deployment.schema.json +++ b/spec/v1/schemas/resolved-deployment.schema.json @@ -1508,7 +1508,14 @@ "egress": { "type": "array", "items": { - "$ref": "#/$defs/EgressPeer" + "anyOf": [ + { + "$ref": "#/$defs/EgressPeer" + }, + { + "$ref": "#/$defs/GateMembersPeer" + } + ] } } }, @@ -2771,9 +2778,23 @@ "tier-proxy", "metrics-stack", "backup", - "flux-events" + "flux-events", + "release-gate" ] }, + "GateMembersPeer": { + "type": "object", + "properties": { + "rule": { + "type": "string", + "const": "gate-members" + } + }, + "required": [ + "rule" + ], + "additionalProperties": false + }, "ResolvedPolicyJob": { "type": "object", "properties": { diff --git a/src/adapters/kubernetes/render.ts b/src/adapters/kubernetes/render.ts index 2ed25b587..4bea625de 100644 --- a/src/adapters/kubernetes/render.ts +++ b/src/adapters/kubernetes/render.ts @@ -57,6 +57,7 @@ import { import { applicationLabels, consumerLabelsOf, + MEMBER_LABEL, instanceOf, labelsOf, managedOnly, @@ -611,6 +612,15 @@ type PodTemplate = Deployment["spec"]["template"]; * The pod a Process runs in, whatever controller starts it: a run to * completion restarts in place on a failure. */ +/** Whether the Process is a member of its Application's release gate. */ +const memberOf = ( + process: ResolvedProcess, + application: ResolvedApplicationDocument, +): boolean => + application.releaseGate?.members.some( + (member) => member.process === process.name, + ) === true; + function podTemplateOf( process: ResolvedProcess, application: ResolvedApplicationDocument, @@ -625,6 +635,7 @@ function podTemplateOf( // A missing list and an empty one carry no label alike. // Stryker disable next-line ArrayDeclaration ...consumerLabelsOf(process.consumes ?? []), + ...(memberOf(process, application) ? MEMBER_LABEL : {}), }, [ [process.name, process.imageVersion], diff --git a/src/adapters/networking/render.ts b/src/adapters/networking/render.ts index 7369e5e2f..d48a6604a 100644 --- a/src/adapters/networking/render.ts +++ b/src/adapters/networking/render.ts @@ -19,6 +19,7 @@ import type { import { consumerLabelsOf, instanceOf, + MEMBER_LABEL, labelsOf, managedOnly, namespaceSelector, @@ -65,6 +66,16 @@ const consumerPeerOf = (consumes: string): PolicyPeer => ({ podSelector: { matchLabels: consumerLabelsOf([consumes]) }, }); +/** + * Every gate member's pods, in any namespace deploy-kit manages: the label is + * rendered only on a Process the release-gate inputs name + * (spec/v1/16-dependencies.md#the-member-label). + */ +const membersPeer: PolicyPeer = { + namespaceSelector: { matchLabels: managedOnly() }, + podSelector: { matchLabels: { ...MEMBER_LABEL } }, +}; + /** * Flux's notification controller is no Process: nothing renders it, and its * pods carry the labels Flux's own install gives them, of which `app` names @@ -163,16 +174,22 @@ function policyOf( ); // The baseline admits UDP and TCP alike, since the cluster's DNS answers on // both; only its peers other than DNS are reached through the mesh. - const baseline = (process.egress ?? []).map((peer) => ({ - to: [peerOf(peer.namespace, peer.process)], - ports: [ - { protocol: "UDP" as const, port: peer.port }, - tcp(peer.port), - ...(mesh === undefined || peer.rule === DNS - ? [] - : [tcp(mesh.tunnelPort)]), - ], - })); + const baseline = (process.egress ?? []).map((peer) => + // The gate's own requests reach every member's pods, on whichever port + // the member's own policy admits them. + peer.rule === "gate-members" + ? { to: [membersPeer] } + : { + to: [peerOf(peer.namespace, peer.process)], + ports: [ + { protocol: "UDP" as const, port: peer.port }, + tcp(peer.port), + ...(mesh === undefined || peer.rule === DNS + ? [] + : [tcp(mesh.tunnelPort)]), + ], + }, + ); // Where the Kubernetes API answers, for a Process that holds access to it: // an address, because the API server is no pod a selector reaches. // A missing block and an empty list admit the same nowhere. diff --git a/src/adapters/shared/labels.ts b/src/adapters/shared/labels.ts index 479468dfd..81056976a 100644 --- a/src/adapters/shared/labels.ts +++ b/src/adapters/shared/labels.ts @@ -35,6 +35,11 @@ export const consumerLabelsOf = (consumes: readonly string[]): Labels => /** The prefix of every consumer label (spec/v1/16-dependencies.md#the-consumer-label). */ export const CONSUMER_LABEL = "consumes.deploy-kit.jorisjonkers.dev/"; +/** The label every gate member's pods carry, which the Release Gate's policy admits (spec/v1/16-dependencies.md#the-member-label). */ +export const MEMBER_LABEL = { + "gated.deploy-kit.jorisjonkers.dev/release-gate": "true", +} as const; + /** The selector every rendered object but a disruption budget uses. */ export const instanceOf = (process: string): Labels => ({ "app.kubernetes.io/instance": process, diff --git a/src/model/resolved-deployment.ts b/src/model/resolved-deployment.ts index 206fe2be7..8d2ff6168 100644 --- a/src/model/resolved-deployment.ts +++ b/src/model/resolved-deployment.ts @@ -70,6 +70,7 @@ export const INGRESS_RULES = [ "metrics-stack", "backup", "flux-events", + "release-gate", ] as const; /** Which rule admits an outbound peer beyond an edge: the baseline, or a grant. */ @@ -279,6 +280,13 @@ const egressPeer = z }) .meta({ id: "EgressPeer" }); +// Every gate member's pods, wherever they run, on any port: what the Release +// Gate's own requests reach (spec/v1/16-dependencies.md#the-member-label). The +// member's own policy names the port it admits them on. +const gateMembersPeer = z + .strictObject({ rule: z.literal("gate-members") }) + .meta({ id: "GateMembersPeer" }); + // An address range a policy admits by address, on one port, because no // namespace or pod is the peer: where an off-cluster copy goes // (spec/v1/14-platform-intent.md#durability-policy), or where the Kubernetes @@ -672,7 +680,7 @@ const resolvedProcess = stated( surfaces: z.array(resolvedSurface).exactOptional(), l4Exposure: z.array(resolvedL4Exposure).exactOptional(), ingress: z.array(ingressPeer).exactOptional(), - egress: z.array(egressPeer).exactOptional(), + egress: z.array(z.union([egressPeer, gateMembersPeer])).exactOptional(), }), "ResolvedProcess", switchoverFollowsCutover, @@ -1166,6 +1174,8 @@ export type ResolvedDeploymentDocument = z.output; export type ResolvedPolicyJob = z.output; export type ResolvedDashboards = z.output; export type ResolvedCatalogJob = z.output; +/** An outbound peer named by namespace, and by Process where one Process is the peer. */ +export type EgressPeer = z.output; export type ResolvedEvents = z.output; export type ResolvedApplicationDocument = z.output< typeof resolvedApplicationDocument diff --git a/src/resolve/application.ts b/src/resolve/application.ts index c518ef9ec..713f55b8a 100644 --- a/src/resolve/application.ts +++ b/src/resolve/application.ts @@ -43,7 +43,7 @@ import { resolveL4Exposure } from "./l4-exposure.ts"; import { l4ExposuresOf, l4HoldOf } from "../model/l4-addresses.ts"; import { managed } from "./database.ts"; import { resolveMigration } from "./migration.ts"; -import { egressOf, ingressOf } from "./policy.ts"; +import { egressOf, gateEgressOf, gateIngressOf, ingressOf } from "./policy.ts"; import { resolveProcess, type ProcessContext } from "./process.ts"; /** An Application's element: the projection without its document header and provenance. */ @@ -493,7 +493,6 @@ export function resolveApplication( // claim, egress and token, before anything reads the Process. const derived = deriveProxy(authored, application, context); const { process } = derived; - const ingress = ingressOf(process, application, context); const clientGrants = [ ...clientGrantsOf(process, application, context), ...derived.grants, @@ -516,6 +515,14 @@ export function resolveApplication( ], held: l4HoldOf(exposures, context.platform), }); + // A blue-green Process that publishes readiness is a gate member, and + // admits the gate's own requests. + const member = + base.switchover === "blue-green" && readinessOf(process) !== undefined; + const ingress = [ + ...ingressOf(process, application, context), + ...gateIngressOf(base, member, context), + ]; return { process, resolved: { @@ -523,7 +530,10 @@ export function resolveApplication( ...(l4Exposure.length === 0 ? {} : { l4Exposure }), ...withEnvironment(base, derived.environment), ...(ingress.length === 0 ? {} : { ingress }), - egress: egressOf(process, context, clientGrants.length > 0), + egress: [ + ...egressOf(process, context, clientGrants.length > 0), + ...gateEgressOf(application, context), + ], }, }; }); diff --git a/src/resolve/database.ts b/src/resolve/database.ts index 4f6f98ef9..5c8141540 100644 --- a/src/resolve/database.ts +++ b/src/resolve/database.ts @@ -8,12 +8,12 @@ import type { EffectiveProject, } from "../model/effective-intent.ts"; import { ownsDatabases } from "../model/project-intent-queries.ts"; -import type { ResolvedProcess } from "../model/resolved-deployment.ts"; +import type { EgressPeer } from "../model/resolved-deployment.ts"; import type { PlatformIntentDocument } from "../model/platform-intent.ts"; import { homeOf } from "../model/runtime-profiles.ts"; import { consumedOf } from "../model/consumer-label.ts"; -type Egress = NonNullable[number]; +type Egress = EgressPeer; /** Whether an Application moves its schema with a changelog the platform's runner applies. */ export const managed = (application: EffectiveApplication): boolean => diff --git a/src/resolve/policy.ts b/src/resolve/policy.ts index 1bf201423..7d682d40a 100644 --- a/src/resolve/policy.ts +++ b/src/resolve/policy.ts @@ -15,7 +15,10 @@ import { holds, type PlatformIntentDocument, } from "../model/platform-intent.ts"; -import type { ResolvedProcess } from "../model/resolved-deployment.ts"; +import type { + EgressPeer, + ResolvedProcess, +} from "../model/resolved-deployment.ts"; import { consumedOf } from "../model/consumer-label.ts"; import { backupIdentityOf, @@ -25,7 +28,11 @@ import { import { dumpedSurfaceOf } from "../model/backup.ts"; type Ingress = NonNullable[number]; -type Egress = NonNullable[number]; +type Egress = EgressPeer; +type GateMembers = Extract< + NonNullable[number], + { rule: "gate-members" } +>; /** The DNS port every egress policy admits (spec/v1/16-dependencies.md#the-baseline). */ export const DNS_PORT = 53; @@ -157,6 +164,44 @@ export function ingressOf( return distinct([...routes, ...metrics, ...consumers, ...backups, ...events]); } +/** + * The Release Gate's own requests to a gate member's new copy + * (spec/v1/16-dependencies.md#the-member-label): every Process of the gate the + * platform names, on the port of the member's first surface, which its Canary + * service targets. + */ +export function gateIngressOf( + resolved: Pick, + member: boolean, + context: PolicyContext, +): Ingress[] { + if (!member) return []; + // A member serves on its first surface, or E_RELEASE_UNIT_NO_READINESS and + // the surface checks refused it. + const [served] = resolved.surfaces as [ + NonNullable[number], + ]; + // A member switches under a delivery policy, or E_NO_DELIVERY_POLICY + // refused it. + const { gate } = context.platform.delivery as NonNullable< + PlatformIntentDocument["delivery"] + >; + return peersOf(context.platform, gate, context.union).map((peer) => ({ + rule: "release-gate", + ...peer, + port: served.port, + })); +} + +/** Every gate member's pods, for each Process of the Release Gate the platform names. */ +export const gateEgressOf = ( + application: Pick, + context: PolicyContext, +): GateMembers[] => + holds(context.platform.delivery?.gate, context.project, application.id) + ? [{ rule: "gate-members" }] + : []; + /** The surface the Secret Store answers every grant on. */ const STORE_SURFACE = "http"; diff --git a/test/application/compose.test.ts b/test/application/compose.test.ts index 058a0063a..662cafbfc 100644 --- a/test/application/compose.test.ts +++ b/test/application/compose.test.ts @@ -308,7 +308,7 @@ describe("composeEstate", () => { ]); expect(FIRST.lock.spec.fragments.notes?.revisions).toEqual({ notes: - "sha256:72cf80f9e8a4e1c12dd833e424aa7c9d5396d184a7c1460666728061b82dc555", + "sha256:1286f40637d2d10a95d3ff5955a5ed51328c334b6928442d492f9ab0f54fb03d", }); expect(FIRST.lock.spec.fragments["jorisjonkers.dev"]?.revisions).toEqual( {}, diff --git a/test/model/render.test.ts b/test/model/render.test.ts index 329d95337..5eb193a68 100644 --- a/test/model/render.test.ts +++ b/test/model/render.test.ts @@ -972,6 +972,58 @@ describe("renderIntentSet", () => { ]); }); + // deploy-kit#602: only a member's pods carry the label the gate's policy + // reaches; a blue-green Process that publishes no readiness is no member. + it("labels a gate member's pods for the Release Gate, and no other Process's", () => { + const worker = ` processes: + - name: notes-worker + lifecycle: application + image: notes-api + runtime: node + provides: { http: 8081 } + placement: { memory: 64Mi, cpu: 10m } + probes: none + startupBudget: 20s + cutover: continuous +`; + const tree = asTree( + artifact( + render({ + "minimal/notes.project.yml": (document) => + document.replace(" processes:\n", worker), + }), + "notes", + ), + ); + const podLabels = Object.fromEntries( + parseAllDocuments(tree["apps/notes/notes/workload.yaml"] ?? "") + .map((document) => document.toJS() as unknown) + .filter( + ( + object, + ): object is { + kind: string; + metadata: { name: string }; + spec: { + template: { metadata: { labels: Record } }; + }; + } => (object as { kind?: string } | null)?.kind === "Deployment", + ) + .map(({ metadata, spec }) => [ + metadata.name, + spec.template.metadata.labels, + ]), + ); + const member = "gated.deploy-kit.jorisjonkers.dev/release-gate"; + + expect(Object.keys(podLabels).sort()).toStrictEqual([ + "notes-api", + "notes-worker", + ]); + expect(podLabels["notes-api"]?.[member]).toBe("true"); + expect(podLabels["notes-worker"]?.[member]).toBeUndefined(); + }); + it("puts a path under estate/ in the _estate artifact, and a Project named for an estate directory in its own", () => { const result = renderIntentSet(files(), { ...OPTIONS, diff --git a/test/model/resolution-derivations.test.ts b/test/model/resolution-derivations.test.ts index 4769fea22..b0629bbd1 100644 --- a/test/model/resolution-derivations.test.ts +++ b/test/model/resolution-derivations.test.ts @@ -1759,9 +1759,10 @@ describe("what a grant derives", () => { it("admits the Secret Store's http surface to a Process that holds a grant, and to no other", () => { const rules = (applications: string) => - processOf(applications).egress?.map( - ({ rule, namespace, process, port }) => - `${rule} ${namespace} ${process ?? "*"} ${String(port)}`, + processOf(applications).egress?.map((peer) => + peer.rule === "gate-members" + ? peer.rule + : `${peer.rule} ${peer.namespace} ${peer.process ?? "*"} ${String(peer.port)}`, ); expect(rules(granted(SELF))).toStrictEqual([ @@ -2966,9 +2967,16 @@ ${serving("notes-api")}`, }); }); +/** Every peer but the gate's own, which every member admits and its own block tests. */ +const beyondTheGate = ( + peers: readonly T[] | undefined, +): T[] | undefined => peers?.filter(({ rule }) => rule !== "release-gate"); + describe("the peers a policy admits", () => { const ingressOf = (all: ResolvedProject[], project: string, index = 0) => - projectNamed(all, project).applications[0]?.processes[index]?.ingress; + beyondTheGate( + projectNamed(all, project).applications[0]?.processes[index]?.ingress, + ); /** The peers a policy names by namespace and Process: every one but a consumer's. */ const named = (all: ResolvedProject[], project: string, index = 0) => ingressOf(all, project, index)?.filter(({ rule }) => rule !== "consumer"); @@ -3021,11 +3029,13 @@ ${serving("notes-other").replace("provides: { http: 8080 }", "provides: { store: projectNamed(all, "notes").applications[0]?.processes ?? []; const store = projectNamed(all, "notes").applications[1]?.processes[0]; const other = projectNamed(all, "notes").applications[2]?.processes[0]; - expect(store?.ingress).toStrictEqual([ + expect(beyondTheGate(store?.ingress)).toStrictEqual([ consumer("notes-store.store", 7000), consumer("notes-store.http", 8080), ]); - expect(other?.ingress).toStrictEqual([consumer("notes-other.store", 7000)]); + expect(beyondTheGate(other?.ingress)).toStrictEqual([ + consumer("notes-other.store", 7000), + ]); expect(api?.consumes).toStrictEqual(["notes-store.store"]); expect(web?.consumes).toBeUndefined(); }); @@ -3105,18 +3115,66 @@ ${serving("notes-api")}${serving("notes-web")}`); projectNamed(all, "notes").applications[0]?.processes ?? []; expect( - api?.ingress?.flatMap((peer) => + beyondTheGate(api?.ingress)?.flatMap((peer) => peer.rule === "consumer" ? [] : [peer.process], ), ).toStrictEqual(["traefik-public"]); expect( - web?.ingress?.flatMap((peer) => + beyondTheGate(web?.ingress)?.flatMap((peer) => peer.rule === "consumer" ? [] : [peer.process], ), ).toStrictEqual(["traefik-lan"]); }); }); +// deploy-kit#602: the gate's own requests reach a member's new copy, and +// neither half names the other's Project. +describe("the Release Gate's own requests", () => { + const fromTheGate = (port: number) => ({ + rule: "release-gate", + namespace: "delivery-system", + process: "release-gate", + port, + }); + const gated = (peers: readonly { readonly rule: string }[] | undefined) => + peers?.filter(({ rule }) => rule === "release-gate"); + + it("admits the gate on the first surface of every member, and on no Process that is none", () => { + const all = projects(` - id: notes + processes: +${serving("notes-api").replace("provides: { http: 8080 }", "provides: { web: 3000, http: 8080 }")}${serving("notes-web")} - id: notes-batch + processes: +${serving("notes-batch").replace("cutover: continuous", "cutover: interrupted")}`); + const [api, web] = + projectNamed(all, "notes").applications[0]?.processes ?? []; + const batch = projectNamed(all, "notes").applications[1]?.processes[0]; + + expect(gated(api?.ingress)).toStrictEqual([fromTheGate(3000)]); + expect(gated(web?.ingress)).toStrictEqual([fromTheGate(8080)]); + // A stop-start Process is no member: Flagger never switches it. + expect(batch?.switchover).toBe("stop-start"); + expect(gated(batch?.ingress)).toStrictEqual([]); + }); + + it("reaches every member from the gate's own Processes, and from nothing else", () => { + const all = projects(` - id: notes + processes: +${serving("notes-api")}`); + const reaches = (project: string, id: string) => + projectNamed(all, project) + .applications.find((candidate) => candidate.id === id) + ?.processes.flatMap(({ egress }) => + (egress ?? []).filter(({ rule }) => rule === "gate-members"), + ); + + expect(reaches("delivery", "release-gate")).toStrictEqual([ + { rule: "gate-members" }, + ]); + expect(reaches("delivery", "flagger")).toStrictEqual([]); + expect(reaches("notes", "notes")).toStrictEqual([]); + }); +}); + describe("a volume and what its class derives", () => { // The Process provides the surface the platform's rabbitmq method dumps. const holding = (volumes: string, engine = "") =>