Compare commits

...
Sign in to create a new pull request.

37 commits

Author SHA1 Message Date
Kirill Ilin
01423fccf1
fix(gateway-api): remove unused standalone envoy-gateway PackageSource
cozystack.envoy-gateway is never referenced in any bundle template.
Envoy Gateway is deployed through cozystack.gateway-application which
has its own envoy-gateway-system component. Keeping the standalone
source would conflict if someone tried to use it (same Helm release
in same namespace).

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-26 14:04:37 +05:00
Kirill Ilin
ad442ebbfe
fix(gateway-api): fix cert-manager-issuers after rebase and document child tenant limitation
The rebase on main reverted cert-manager-issuers to the old inline
format. Restore the helper-based template with gateway solver support.

Add comment documenting that Gateway API ACME HTTP-01 only works for
the root tenant and system services. Child tenants with gateway: true
should use dns01 solver or namespace-scoped Issuers.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-26 09:39:07 +05:00
Kirill Ilin
f361e37123
chore(tenant): regenerate with cozyvalues-gen v1.3.0
Regenerated values.schema.json, cozyrds/tenant.yaml, and Go types
using cozyvalues-gen v1.3.0 to match CI pre-commit checks.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:21:58 +05:00
Kirill Ilin
b7deea9d74
fix(gateway-api): address round 2 review — comments and fail on misconfiguration
Add architecture documentation comment to extra/gateway explaining the
two-Gateway model on port 80, mergeGateways reliance, and why one
rejected parentRef per ACME challenge is expected.

Add comment to cert-manager-issuers explaining dual parentRef strategy.

Replace silent ingress fallback with fail() when both ingress and
gatewayAPI are disabled — catches misconfiguration at template time.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:43 +05:00
Kirill Ilin
07d841b60e
fix(gateway-api): add per-service HTTP redirect routes for system services
System services (dashboard, keycloak) need HTTP→HTTPS redirect through
the acme-challenge Gateway. Since system namespaces have the
cozystack.io/system label, their HTTPRoutes can attach to the
acme-challenge Gateway (from: Selector).

Tenant services use the http-redirect Gateway (from: Same) for redirect.

This keeps the security model intact: only system namespaces can attach
to acme-challenge, only the gateway's own namespace can attach to
http-redirect.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:43 +05:00
Kirill Ilin
ba43da6655
fix(gateway-api): address review findings
- Restrict acme-challenge Gateway allowedRoutes to system namespaces
  (cozystack.io/system label selector) to prevent tenants from
  intercepting HTTP traffic. Add separate http-redirect Gateway with
  from: Same for the redirect route and same-namespace ACME challenges.
  ClusterIssuer solver references both Gateways so cert-manager
  challenges work for both system namespaces (via Selector) and the
  gateway's own tenant namespace (via Same).
- Add gateway-tlsroute to external-dns sources so TLSRoute services
  (kubernetes-api, vm-exportproxy, cdi-uploadproxy) get DNS records.
- Make gateway-application conditional on gateway.gatewayAPI to avoid
  deploying Envoy Gateway when not needed.
- Add fallback ingress solver when both ingress and gatewayAPI are
  disabled to prevent empty solvers list in ClusterIssuer.
- Make KC_SPI_STICKY_SESSION env conditional on gateway-api to avoid
  affecting ingress-only deployments.
- Remove HTTP listeners and redirect routes from alerta and grafana
  Gateways (same fix as dashboard/keycloak/bucket/harbor).
- Replace flux reconcile with kubectl annotate in e2e for consistency.
- Fix bootbox HTTPRoute port: 8080 → 80 to match service definition.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:43 +05:00
Kirill Ilin
f599d32162
fix(gateway-api): use self-signed issuer in e2e for Gateway TLS
Per-component Gateways now only have HTTPS listeners which require a
valid TLS certificate to become ready. In e2e with example.org domain,
ACME challenges can't be completed so certificates are never issued and
HTTPS listeners stay not-ready, causing HTTPRoute acceptance to fail.

Switch to selfsigned-cluster-issuer in e2e so certificates are issued
immediately without ACME verification.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:43 +05:00
Kirill Ilin
9f7c653c4b
fix(gateway-api): fix e2e test blocking and Gateway API resource names
Replace blocking flux reconcile --force with non-blocking annotation to
avoid 5-minute timeout when dashboard HR has unresolved dependencies.

Use fully qualified gateways.gateway.networking.k8s.io resource name to
avoid conflict with cozystack Gateway CRD short name.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:43 +05:00
Kirill Ilin
c7bc418974
fix(gateway-api): correct keycloak HTTPRoute backend port
The keycloak-http service listens on port 8080, not 80. This caused
Envoy Gateway to return 500 for all keycloak requests via Gateway API.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:43 +05:00
Kirill Ilin
086ceda69b
fix(gateway-api): move HTTP listener and redirect to shared acme-challenge Gateway
Per-component Gateways had HTTP listeners with hostname-specific redirect
routes that conflicted with cert-manager ACME HTTP-01 challenges. The
redirect captured HTTP traffic before the challenge HTTPRoute could serve
the token, creating a chicken-and-egg problem.

Move the HTTP listener to a single shared acme-challenge Gateway with a
wildcard listener (no hostname restriction, allowedRoutes from All). Add
a global redirect-to-https HTTPRoute on this Gateway. Per-component
Gateways now only have HTTPS listeners.

The ACME challenge Exact path match takes priority over the redirect's
implicit PathPrefix, so challenges are served correctly while all other
HTTP traffic is redirected to HTTPS.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:43 +05:00
Kirill Ilin
a06d24ba1a
fix(gateway-api): add ACME challenge Gateway and gatewayHTTPRoute solver
cert-manager requires a parentRef in the gatewayHTTPRoute solver to know
which Gateway should serve the ACME HTTP-01 challenge. Without it, the
webhook rejects the ClusterIssuer.

Add a shared acme-challenge Gateway in the extra/gateway chart with a
wildcard HTTP listener (allowedRoutes from All namespaces). This Gateway
merges with other per-component Gateways via mergeGateways and serves
ACME challenges for all certificates regardless of namespace.

Update the ClusterIssuer template to reference this Gateway when
gatewayAPI is enabled.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:42 +05:00
Kirill Ilin
58302b5d20
fix(gateway-api): add Gateway API HTTP-01 solver to ClusterIssuers
When gatewayAPI is enabled, cert-manager needs a gatewayHTTPRoute solver
to issue certificates for Gateway listeners. Previously only the Ingress
solver was configured, causing ACME challenges to fail when Ingress is
disabled.

The solver list is now dynamic based on platform settings:
- ingress-enabled=true: adds http01.ingress solver
- gateway-api=true: adds http01.gatewayHTTPRoute solver
- both enabled: both solvers are present
- dns01: unchanged, uses Cloudflare solver

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:42 +05:00
Kirill Ilin
ca929dc92d
fix(gateway-api): use actual cluster domain for Envoy Gateway xDS
Envoy Gateway subchart hardcodes kubernetesClusterDomain to cluster.local,
but cozystack clusters use a configurable domain (e.g. cozy.local). This
causes data plane pods to fail connecting to the xDS control plane because
the DNS name in the bootstrap config doesn't resolve.

Add global.clusterDomain to cozystack-values secret from the platform's
networking.clusterDomain setting. Patch the subchart deployment and certgen
templates to prefer global.clusterDomain over the hardcoded default.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:42 +05:00
Kirill Ilin
d4307b4f36
fix(gateway-api): force reconcile system HRs and increase gateway timeout in e2e
After enabling gatewayAPI on the platform, system HelmReleases need to
pick up the new gateway-api value from cozystack-values secret. Force
reconcile dashboard and cozystack-api to avoid waiting up to 5 minutes
for the next Flux reconciliation cycle.

Increase gateway address timeout from 120s to 300s to allow time for
Envoy data plane provisioning.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:42 +05:00
Kirill Ilin
4e0576e1ae
fix(gateway-api): install Envoy Gateway CRDs via templates
Helm 3 does not process CRDs from subchart crds/ directories in vanilla
Helm, while Flux does process them causing conflicts with the existing
gateway-api-crds package.

Move only Envoy-specific CRDs (gateway.envoyproxy.io) into templates/crds/
so they are installed and updated as regular resources. Remove the subchart
crds/ directory to prevent Flux from double-applying standard Gateway API
CRDs already provided by the gateway-api-crds package.

Update Makefile to copy Envoy CRDs and remove subchart crds/ after
helm pull.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:42 +05:00
Kirill Ilin
2082c79e70
fix(gateway-api): deploy envoy-gateway controller and remove unused cozystack-gateway
Add install block to envoy-gateway-system component in gateway-application
PackageSource so the operator actually creates a HelmRelease for Envoy Gateway.
Without this, the EnvoyProxy CRD was never installed, causing tenant gateway
HelmReleases to fail with "no matches for kind EnvoyProxy".

Remove the unused cozystack-gateway chart and PackageSource since the PR uses
per-component Gateway architecture where each service creates its own Gateway
resource, making the central Gateway redundant.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:42 +05:00
Kirill Ilin
4b68573898
fix(gateway-api): update e2e tests for Envoy Gateway architecture
- Use --resolve for correct TLS SNI in curl probes
- Update central Gateway test to per-tenant Gateway model
- Reference per-component Gateway (dashboard) instead of cozystack-gateway
- Remove duplicate tenant patch in Grafana test

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:42 +05:00
Kirill Ilin
e35a886328
feat(gateway-api): add Envoy Gateway support with per-tenant model
Add Envoy Gateway as the Gateway API controller with mergeGateways
support. Per-tenant gateway model mirrors ingress-nginx architecture.

Per-tenant gateway model:
- gateway: true in tenant -> GatewayClass + EnvoyProxy per tenant
- mergeGateways: true -> all Gateways per tenant merge into one Service
- When externalIPs configured: ClusterIP + externalIPs (same as nginx)
- When no externalIPs: standard LoadBalancer
- gateway: false -> inherit parent's GatewayClass

New packages:
- packages/system/envoy-gateway/ (Envoy Gateway controller v1.7.1)
- packages/extra/gateway/ (per-tenant GatewayClass + EnvoyProxy)
- packages/system/gateway-rd/ (ApplicationDefinition for dashboard UI)
- packages/core/platform/sources/envoy-gateway.yaml (standalone)
- packages/core/platform/sources/gateway-application.yaml (bundled)
- packages/apps/tenant/templates/gateway.yaml (tenant HelmRelease)

Platform changes:
- Add gateway.gatewayClass field for system services fallback
- All components use _namespace.gateway as gatewayClassName
- System services fall back to _cluster.gateway when _namespace absent
- System services create per-component Gateways

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:42 +05:00
Kirill Ilin
9aa1a428d2
chore(tenant): make generate
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:42 +05:00
Kirill Ilin
7186ac805d
test(gateway-api): add e2e tests for Gateway API
Cover central Gateway, system HTTPRoutes/TLSRoutes, HTTP-to-HTTPS
redirect, TLS passthrough to API server, per-tenant Grafana Gateway,
and ingress coexistence verification.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:34 +05:00
Kirill Ilin
c372e858bf
feat(gateway-api): add CiliumLoadBalancerIPPool for externalIPs when ingress disabled
When ingress is disabled and Gateway API is used, create a
CiliumLoadBalancerIPPool from publishing.externalIPs so that Cilium
assigns those IPs to the Gateway's LoadBalancer Service.

Priority logic: ingress takes externalIPs when enabled; Gateway gets
them only when ingress is off.

Propagate gateway.ingress as ingress-enabled in _cluster values so the
gateway chart can make this decision.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:34 +05:00
Kirill Ilin
4381a09b86
fix(gateway-api): avoid passing empty components dict to platform.package
cozystack.platform.package renders `components: {}` when given an
empty dict, which causes the operator post-render to fail with YAML
unmarshal errors. Use package.default (no components) when the dict
is empty, package (with components) only when non-empty.

Fixes cert-manager and external-dns conditional components blocks
that pass an empty dict when gateway.gatewayAPI is false.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:34 +05:00
Kirill Ilin
dff5ab0904
revert: temporarily remove cozy-lib Gateway API helpers
Revert cozy-lib refactoring to debug platform upgrade issue.
Gateway templates restored to inline form. cozy-lib dependency
removed from bucket, cozystack-gateway, monitoring, harbor,
bootbox, seaweedfs charts.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:34 +05:00
Kirill Ilin
32b3fe437e
fix(gateway-api): use trailing slash in cozy-lib symlinks
Match the existing symlink convention used by other charts
(e.g. keycloak, tenant) where the cozy-lib symlink target
includes a trailing slash: ../../../library/cozy-lib/

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:34 +05:00
Kirill Ilin
f728510df2
feat(gateway-api): configure Cilium and Keycloak for Gateway API gaps
When gateway.gatewayAPI is enabled in platform config:

- Set Cilium envoy.streamIdleTimeoutDurationSeconds to 86400 (24h)
  to support long-lived WebSocket (dashboard/noVNC) and large file
  uploads (bucket, harbor, seaweedfs). Cilium does not support
  per-route timeouts (GEP-1742), so a global override is needed.

- Enable gatewayAPI.enableAlpn for HTTP/2 and gRPC support through
  Gateway API listeners (required by seaweedfs filer gRPC backend).

- Disable Keycloak sticky session route attachment
  (KC_SPI_STICKY_SESSION_ENCODER_INFINISPAN_SHOULD_ATTACH_ROUTE=false)
  so Keycloak works without cookie-based session affinity. Infinispan
  distributed cache handles session replication across nodes. This
  removes the dependency on nginx cookie affinity annotations.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:34 +05:00
Kirill Ilin
7ab2f90f1d
refactor(gateway-api): extract Gateway API helpers into cozy-lib
Add cozy-lib.gateway.httpRoute and cozy-lib.gateway.tlsRoute helpers
that encapsulate the full Gateway + HTTPRoute/TLSRoute pattern with
HTTP-to-HTTPS redirect. This eliminates ~350 lines of duplicated
boilerplate across 6 tenant-level components.

Add cozy-lib as dependency to bucket, monitoring, harbor, bootbox,
seaweedfs, and cozystack-gateway charts. Update PackageSource
definitions for cozystack-gateway and bucket-system to include
cozy-lib library.

Add ns-gateway helper to _cozyconfig.tpl for consistency with
existing ns-ingress, ns-etcd, etc.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:34 +05:00
Kirill Ilin
cba6538746
refactor(gateway-api): make gatewayClassName configurable
Replace hardcoded `gatewayClassName: cilium` in all 7 Gateway
templates with a configurable value from _cluster.gateway-class-name.
Default remains "cilium" for backward compatibility.

This enables custom GatewayClass resources with different parameters
(e.g. NodePort via CiliumGatewayClassConfig) and maintains
vendor-neutrality for non-Cilium Gateway API controllers.

Assisted-By: Claude AI
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:34 +05:00
Kirill Ilin
8c328c6ed1
fix(gateway-api): use stable v1 apiVersion for TLSRoute
v1alpha2 is deprecated in Gateway API v1.5.0 with a removal warning.
Update all TLSRoute resources to use gateway.networking.k8s.io/v1.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:34 +05:00
Kirill Ilin
bd2f340b8e
chore(tenant) make generate for gateway
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:14:34 +05:00
Kirill Ilin
bb332c4774
fix(gateway-api): address review findings
- Add HTTP-to-HTTPS redirect HTTPRoute for all Gateways (central and
  per-tenant) to ensure plain HTTP requests are properly redirected
- Add oidc-enabled check to keycloak HTTPRoute condition to match
  the Gateway listener condition
- Use gateway-name from cluster config instead of hardcoded value
  in central Gateway template
- Remove redundant namespace from TLSRoute metadata where it matches
  the HelmRelease install namespace (kubevirt, kubevirt-cdi)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:13:56 +05:00
Kirill Ilin
fb06e0ce1d
feat(gateway): add Gateway API routes for tenant-level services
Add per-component Gateway + HTTPRoute/TLSRoute for tenant-level
services. Each component creates its own Gateway with specific
hostname HTTPS listener and cert-manager annotation for automatic
certificate provisioning via HTTP01 or DNS01.

All Gateways within a tenant share a single LoadBalancer IP through
infrastructure.labels (cozystack.io/gateway: {tenant-name}), matching
the current ingress-nginx model of one LB per tenant.

HTTPRoute (per-component Gateway with TLS terminate):
- monitoring/grafana: routes to grafana-service:3000
- monitoring/alerta: routes to alerta:80
- bucket: routes to {bucketName}-ui:8080
- apps/harbor: routes to {release}:80
- extra/bootbox: routes to bootbox:8080

TLSRoute (per-component Gateway with TLS passthrough):
- extra/seaweedfs: passthrough to filer-external:18888

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:13:56 +05:00
Kirill Ilin
73965bfa06
feat(gateway): add _namespace.gateway to tenant model
Add gateway field to tenant namespace configuration, following the
same inheritance pattern as ingress:
- tenant-root defaults to gateway: tenant-root
- Sub-tenants with gateway: true get their own LB
- Sub-tenants without gateway inherit from parent

Add infrastructure.labels to central Gateway for shared LB with
tenant-root services (cozystack.io/gateway: tenant-root).

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:13:56 +05:00
Kirill Ilin
fa9dddf7ea
feat(gateway): add HTTPRoute/TLSRoute for system services
Add Gateway API route templates alongside existing Ingress templates
for all system-level services. Routes are conditional on
_cluster.gateway-api being enabled.

HTTPRoute (TLS terminate via central Gateway):
- dashboard: routes to incloud-web-gatekeeper:8000
- keycloak: routes to keycloak-http:80

TLSRoute (TLS passthrough via central Gateway):
- cozystack-api: passthrough to kubernetes:443
- kubevirt vm-exportproxy: passthrough to vm-exportproxy:443
- kubevirt-cdi uploadproxy: passthrough to cdi-uploadproxy:443

All routes reference the central Gateway via parentRefs with
sectionName for listener matching. Hostname resolution follows
convention ({service}.{root-host}) with override support via
_cluster.hostnames map.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:13:56 +05:00
Kirill Ilin
dcbe3e6d01
feat(gateway): add central Gateway resource and platform config
Add cozystack-gateway system package that creates a shared Gateway
resource with per-hostname HTTPS listeners and TLS passthrough
listeners for system services. Listeners are dynamically generated
from expose-services config.

Platform changes:
- Add gateway-api flag to _cluster config for component awareness
- Add gateway.name/namespace to platform values for parentRef config
- Add publishing.hostnames map for per-service hostname overrides
- Wire cozystack-gateway package into system bundle (conditional)

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:13:56 +05:00
Kirill Ilin
f030194b88
fix(cert-manager): make enableGatewayAPI conditional on gateway.gatewayAPI
Previously enableGatewayAPI was hardcoded to true in cert-manager values,
causing startup failures in isp-hosted variant where Gateway API CRDs
are not installed. Now passed via component override only when
gateway.gatewayAPI is enabled.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:13:56 +05:00
Kirill Ilin
ba2f5eb935
feat(external-dns): add Gateway API source when gatewayAPI is enabled
When gateway.gatewayAPI is true, pass gateway-httproute source to the
system external-dns package via components values override.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:13:56 +05:00
Kirill Ilin
6a64c3d7d4
feat(gateway-api): add Gateway API support via Cilium
- Upgrade Gateway API CRDs from v1.2.0 to v1.5.0 (experimental channel)
- Add gateway-api-crds as a component in all networking variants,
  with Cilium depending on it to ensure CRDs are available first
- Add gateway.gatewayAPI platform config (enables Cilium
  gatewayAPI.enabled=true), can be used alongside gateway.ingress
- Enable Gateway API support in cert-manager

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
2026-03-25 22:13:55 +05:00
73 changed files with 42554 additions and 1334 deletions

View file

@ -29,6 +29,9 @@ type ConfigSpec struct {
// Deploy own Ingress Controller.
// +kubebuilder:default:=false
Ingress bool `json:"ingress"`
// Deploy own Gateway API gateway (separate LB for this tenant).
// +kubebuilder:default:=false
Gateway bool `json:"gateway"`
// Deploy own SeaweedFS.
// +kubebuilder:default:=false
Seaweedfs bool `json:"seaweedfs"`

View file

@ -200,6 +200,130 @@ EOF
kubectl wait hr/keycloak hr/keycloak-configure hr/keycloak-operator -n cozy-keycloak --timeout=10m --for=condition=ready
}
@test "Enable Gateway API and verify per-tenant Gateway" {
# Enable Gateway API on platform with self-signed issuer (example.org can't use ACME)
kubectl patch package cozystack.cozystack-platform --type merge -p '{"spec":{"components":{"platform":{"values":{"gateway":{"gatewayAPI":true},"publishing":{"certificates":{"issuerName":"selfsigned-cluster-issuer"}}}}}}}'
# Enable gateway on root tenant
kubectl patch tenants/root -n tenant-root --type merge -p '{"spec":{"gateway":true}}'
kubectl wait hr/tenant-root -n tenant-root --timeout=2m --for=condition=ready
# Wait for per-tenant gateway HelmRelease to appear and become ready
timeout 120 sh -ec 'until kubectl get hr -n tenant-root gateway >/dev/null 2>&1; do sleep 1; done'
kubectl wait hr/gateway -n tenant-root --timeout=5m --for=condition=ready
# Verify GatewayClass created and accepted
timeout 60 sh -ec 'until [ "$(kubectl get gatewayclass tenant-root -o jsonpath='"'"'{.status.conditions[?(@.type=="Accepted")].status}'"'"' 2>/dev/null)" = "True" ]; do sleep 1; done'
# Trigger reconcile of system HelmReleases so they pick up gateway-api: true
# Use annotation instead of flux reconcile --force to avoid blocking on dependencies
kubectl annotate hr dashboard -n cozy-dashboard reconcile.fluxcd.io/requestedAt="$(date +%s)" --overwrite
kubectl annotate hr cozystack-api -n cozy-cozystack-api reconcile.fluxcd.io/requestedAt="$(date +%s)" --overwrite || true
# Wait for a per-component Gateway to get an address (merged Service)
timeout 300 sh -ec 'until [ -n "$(kubectl get gateways.gateway.networking.k8s.io dashboard -n cozy-dashboard -o jsonpath='"'"'{.status.addresses[0].value}'"'"' 2>/dev/null)" ]; do sleep 1; done'
gateway_ip=$(kubectl get gateways.gateway.networking.k8s.io dashboard -n cozy-dashboard -o jsonpath='{.status.addresses[0].value}')
if [ -z "$gateway_ip" ]; then
echo "Gateway has no IP address assigned" >&2
kubectl get gateways.gateway.networking.k8s.io dashboard -n cozy-dashboard -o yaml >&2
exit 1
fi
echo "Gateway IP: $gateway_ip"
}
@test "Verify system HTTPRoutes and TLSRoutes via Gateway" {
# Dashboard HTTPRoute
if ! timeout 60 sh -ec 'until [ "$(kubectl get httproute dashboard-web -n cozy-dashboard -o jsonpath='"'"'{.status.parents[0].conditions[?(@.type=="Accepted")].status}'"'"' 2>/dev/null)" = "True" ]; do sleep 1; done'; then
echo "Dashboard HTTPRoute not accepted by Gateway" >&2
kubectl get httproute dashboard-web -n cozy-dashboard -o yaml >&2
exit 1
fi
# Keycloak HTTPRoute
if ! timeout 60 sh -ec 'until [ "$(kubectl get httproute keycloak -n cozy-keycloak -o jsonpath='"'"'{.status.parents[0].conditions[?(@.type=="Accepted")].status}'"'"' 2>/dev/null)" = "True" ]; do sleep 1; done'; then
echo "Keycloak HTTPRoute not accepted by Gateway" >&2
kubectl get httproute keycloak -n cozy-keycloak -o yaml >&2
exit 1
fi
# Kubernetes API TLSRoute
if ! timeout 60 sh -ec 'until [ "$(kubectl get tlsroute kubernetes-api -n default -o jsonpath='"'"'{.status.parents[0].conditions[?(@.type=="Accepted")].status}'"'"' 2>/dev/null)" = "True" ]; do sleep 1; done'; then
echo "API TLSRoute not accepted by Gateway" >&2
kubectl get tlsroute kubernetes-api -n default -o yaml >&2
exit 1
fi
}
@test "Access services via Gateway API" {
# With mergeGateways, all per-component Gateways share one Service
# Get the merged Service IP from any Gateway's address
gateway_ip=$(kubectl get gateways.gateway.networking.k8s.io dashboard -n cozy-dashboard -o jsonpath='{.status.addresses[0].value}')
# HTTP-to-HTTPS redirect (301) via system redirect HTTPRoute on acme-challenge Gateway
http_code=$(curl -sS --resolve "dashboard.example.org:80:${gateway_ip}" \
"http://dashboard.example.org" --max-time 10 -o /dev/null -w '%{http_code}')
if [ "$http_code" != "301" ]; then
echo "Expected HTTP 301 redirect, got ${http_code}" >&2
exit 1
fi
# Dashboard via HTTPS (302/303 redirect to Keycloak is expected when OIDC is enabled)
http_code=$(curl -sS -k --resolve "dashboard.example.org:443:${gateway_ip}" \
"https://dashboard.example.org" --max-time 30 -o /dev/null -w '%{http_code}')
if [ "$http_code" != "200" ] && [ "$http_code" != "302" ] && [ "$http_code" != "303" ]; then
echo "Failed to access Dashboard via Gateway, got HTTP ${http_code}" >&2
exit 1
fi
# Kubernetes API via TLS passthrough (401/403 expected without credentials)
http_code=$(curl -sS -k --resolve "api.example.org:443:${gateway_ip}" \
"https://api.example.org" --max-time 30 -o /dev/null -w '%{http_code}')
if [ "$http_code" != "401" ] && [ "$http_code" != "403" ]; then
echo "Expected HTTP 401 or 403 from API server via Gateway, got ${http_code}" >&2
exit 1
fi
}
@test "Verify Grafana via tenant Gateway" {
# gateway: true already set in previous test
# Wait for monitoring to reconcile with gateway config
if ! kubectl wait hr/monitoring -n tenant-root --timeout=3m --for=condition=ready; then
kubectl annotate hr monitoring -n tenant-root reconcile.fluxcd.io/requestedAt="$(date +%s)" --overwrite
kubectl wait hr/monitoring -n tenant-root --timeout=2m --for=condition=ready
fi
# Wait for Grafana per-component Gateway to be Programmed
timeout 120 sh -ec 'until kubectl get gateways.gateway.networking.k8s.io grafana -n tenant-root >/dev/null 2>&1; do sleep 1; done'
kubectl wait gateways.gateway.networking.k8s.io/grafana -n tenant-root --timeout=2m --for=condition=Programmed
# Verify Grafana HTTPRoute is accepted
if ! timeout 60 sh -ec 'until [ "$(kubectl get httproute grafana -n tenant-root -o jsonpath='"'"'{.status.parents[0].conditions[?(@.type=="Accepted")].status}'"'"' 2>/dev/null)" = "True" ]; do sleep 1; done'; then
echo "Grafana HTTPRoute not accepted" >&2
kubectl get httproute grafana -n tenant-root -o yaml >&2
exit 1
fi
# Access Grafana via tenant Gateway (merged Service)
timeout 60 sh -ec 'until [ -n "$(kubectl get gateways.gateway.networking.k8s.io grafana -n tenant-root -o jsonpath='"'"'{.status.addresses[0].value}'"'"' 2>/dev/null)" ]; do sleep 1; done'
grafana_gw_ip=$(kubectl get gateways.gateway.networking.k8s.io grafana -n tenant-root -o jsonpath='{.status.addresses[0].value}')
if ! curl -sS -k --resolve "grafana.example.org:443:${grafana_gw_ip}" \
"https://grafana.example.org" --max-time 30 | grep -q Found; then
echo "Failed to access Grafana via Gateway at ${grafana_gw_ip}" >&2
exit 1
fi
}
@test "Ingress still works alongside Gateway API" {
ingress_ip=$(kubectl get svc root-ingress-controller -n tenant-root -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
if ! curl -sS -k --resolve "grafana.example.org:443:${ingress_ip}" \
"https://grafana.example.org" --max-time 30 | grep -q Found; then
echo "Ingress broken after enabling Gateway API" >&2
exit 1
fi
}
@test "Create tenant with isolated mode enabled" {
kubectl -n tenant-root get tenants.apps.cozystack.io test ||
kubectl apply -f - <<EOF

View file

@ -0,0 +1,52 @@
{{- $gatewayAPI := (index .Values._cluster "gateway-api") | default "false" }}
{{- $clusterIssuer := (index .Values._cluster "issuer-name") | default "letsencrypt-prod" }}
{{- $gateway := .Values._namespace.gateway | default "" }}
{{- $host := .Values._namespace.host }}
{{- $harborHost := .Values.host | default (printf "%s.%s" .Release.Name $host) }}
{{- $gatewayClassName := .Values._namespace.gateway | default "" }}
{{- if and (eq $gatewayAPI "true") (ne $gateway "") }}
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: {{ .Release.Name }}-harbor
annotations:
cert-manager.io/cluster-issuer: {{ $clusterIssuer }}
spec:
gatewayClassName: {{ $gatewayClassName }}
infrastructure:
labels:
cozystack.io/gateway: {{ $gateway }}
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: {{ $harborHost | quote }}
tls:
mode: Terminate
certificateRefs:
- name: {{ .Release.Name }}-harbor-gateway-tls
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: {{ .Release.Name }}-harbor
spec:
parentRefs:
- name: {{ .Release.Name }}-harbor
hostnames:
- {{ $harborHost | quote }}
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: {{ .Release.Name }}
port: 80
{{- end }}

View file

@ -80,6 +80,7 @@ tenant-u1
| `etcd` | Deploy own Etcd cluster. | `bool` | `false` |
| `monitoring` | Deploy own Monitoring Stack. | `bool` | `false` |
| `ingress` | Deploy own Ingress Controller. | `bool` | `false` |
| `gateway` | Deploy own Gateway API gateway (separate LB for this tenant). | `bool` | `false` |
| `seaweedfs` | Deploy own SeaweedFS. | `bool` | `false` |
| `schedulingClass` | The name of a SchedulingClass CR to apply scheduling constraints for this tenant's workloads. | `string` | `""` |
| `resourceQuotas` | Define resource quotas for the tenant. | `map[string]quantity` | `{}` |

View file

@ -0,0 +1,32 @@
{{- if .Values.gateway }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: gateway
namespace: {{ include "tenant.name" . }}
labels:
sharding.fluxcd.io/key: tenants
internal.cozystack.io/tenantmodule: "true"
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
apps.cozystack.io/application.kind: Gateway
apps.cozystack.io/application.group: apps.cozystack.io
apps.cozystack.io/application.name: gateway
spec:
chartRef:
kind: ExternalArtifact
name: cozystack-gateway-application-default-gateway
namespace: cozy-system
interval: 5m
timeout: 10m
install:
remediation:
retries: -1
upgrade:
force: true
remediation:
retries: -1
valuesFrom:
- kind: Secret
name: cozystack-values
{{- end }}

View file

@ -29,6 +29,11 @@
{{- $ingress = $tenantName }}
{{- end }}
{{- $gateway := $parentNamespace.gateway | default "" }}
{{- if .Values.gateway }}
{{- $gateway = $tenantName }}
{{- end }}
{{- $monitoring := $parentNamespace.monitoring | default "" }}
{{- if .Values.monitoring }}
{{- $monitoring = $tenantName }}
@ -61,6 +66,7 @@ metadata:
{{/* Labels for network policies */}}
namespace.cozystack.io/etcd: {{ $etcd | quote }}
namespace.cozystack.io/ingress: {{ $ingress | quote }}
namespace.cozystack.io/gateway: {{ $gateway | quote }}
namespace.cozystack.io/monitoring: {{ $monitoring | quote }}
namespace.cozystack.io/seaweedfs: {{ $seaweedfs | quote }}
namespace.cozystack.io/host: {{ $computedHost | quote }}
@ -92,6 +98,7 @@ stringData:
_namespace:
etcd: {{ $etcd | quote }}
ingress: {{ $ingress | quote }}
gateway: {{ $gateway | quote }}
monitoring: {{ $monitoring | quote }}
seaweedfs: {{ $seaweedfs | quote }}
host: {{ $computedHost | quote }}

View file

@ -22,6 +22,11 @@
"type": "boolean",
"default": false
},
"gateway": {
"description": "Deploy own Gateway API gateway (separate LB for this tenant).",
"type": "boolean",
"default": false
},
"seaweedfs": {
"description": "Deploy own SeaweedFS.",
"type": "boolean",

View file

@ -14,6 +14,9 @@ monitoring: false
## @param {bool} ingress - Deploy own Ingress Controller.
ingress: false
## @param {bool} gateway - Deploy own Gateway API gateway (separate LB for this tenant).
gateway: false
## @param {bool} seaweedfs - Deploy own SeaweedFS.
seaweedfs: false

View file

@ -0,0 +1,33 @@
---
apiVersion: cozystack.io/v1alpha1
kind: PackageSource
metadata:
name: cozystack.gateway-application
spec:
sourceRef:
kind: OCIRepository
name: cozystack-packages
namespace: cozy-system
path: /
variants:
- name: default
dependsOn:
- cozystack.networking
- cozystack.cert-manager
libraries:
- name: cozy-lib
path: library/cozy-lib
components:
- name: envoy-gateway-system
path: system/envoy-gateway
install:
namespace: cozy-envoy-gateway
releaseName: envoy-gateway
- name: gateway
path: extra/gateway
libraries: ["cozy-lib"]
- name: gateway-rd
path: system/gateway-rd
install:
namespace: cozy-system
releaseName: gateway-rd

View file

@ -15,6 +15,12 @@ spec:
- name: cilium
dependsOn: []
components:
- name: gateway-api-crds
path: system/gateway-api-crds
install:
namespace: cozy-gateway-api-crds
releaseName: gateway-api-crds
dependsOn: []
- name: cilium
path: system/cilium
valuesFiles:
@ -24,7 +30,8 @@ spec:
privileged: true
namespace: cozy-cilium
releaseName: cilium
dependsOn: []
dependsOn:
- gateway-api-crds
- name: cilium-networkpolicy
path: system/cilium-networkpolicy
install:
@ -36,6 +43,12 @@ spec:
- name: cilium-kilo
dependsOn: []
components:
- name: gateway-api-crds
path: system/gateway-api-crds
install:
namespace: cozy-gateway-api-crds
releaseName: gateway-api-crds
dependsOn: []
- name: cilium
path: system/cilium
valuesFiles:
@ -46,7 +59,8 @@ spec:
privileged: true
namespace: cozy-cilium
releaseName: cilium
dependsOn: []
dependsOn:
- gateway-api-crds
- name: kilo
path: system/kilo
valuesFiles:
@ -62,6 +76,12 @@ spec:
- name: cilium-generic
dependsOn: []
components:
- name: gateway-api-crds
path: system/gateway-api-crds
install:
namespace: cozy-gateway-api-crds
releaseName: gateway-api-crds
dependsOn: []
- name: cilium
path: system/cilium
valuesFiles:
@ -70,7 +90,8 @@ spec:
privileged: true
namespace: cozy-cilium
releaseName: cilium
dependsOn: []
dependsOn:
- gateway-api-crds
- name: cilium-networkpolicy
path: system/cilium-networkpolicy
install:
@ -82,6 +103,12 @@ spec:
- name: kubeovn-cilium
dependsOn: []
components:
- name: gateway-api-crds
path: system/gateway-api-crds
install:
namespace: cozy-gateway-api-crds
releaseName: gateway-api-crds
dependsOn: []
- name: cilium
path: system/cilium
valuesFiles:
@ -92,7 +119,8 @@ spec:
privileged: true
namespace: cozy-cilium
releaseName: cilium
dependsOn: []
dependsOn:
- gateway-api-crds
- name: cilium-networkpolicy
path: system/cilium-networkpolicy
install:
@ -113,6 +141,12 @@ spec:
- name: kubeovn-cilium-generic
dependsOn: []
components:
- name: gateway-api-crds
path: system/gateway-api-crds
install:
namespace: cozy-gateway-api-crds
releaseName: gateway-api-crds
dependsOn: []
- name: cilium
path: system/cilium
valuesFiles:
@ -122,7 +156,8 @@ spec:
privileged: true
namespace: cozy-cilium
releaseName: cilium
dependsOn: []
dependsOn:
- gateway-api-crds
- name: cilium-networkpolicy
path: system/cilium-networkpolicy
install:

View file

@ -18,6 +18,8 @@ metadata:
type: Opaque
stringData:
values.yaml: |
global:
clusterDomain: {{ .Values.networking.clusterDomain | quote }}
_cluster:
root-host: {{ $rootHost | quote }}
bundle-name: {{ .Values.bundles.system.variant | quote }}
@ -40,6 +42,13 @@ stringData:
scheduling:
{{- . | toYaml | nindent 8 }}
{{- end }}
ingress-enabled: {{ .Values.gateway.ingress | quote }}
gateway-api: {{ .Values.gateway.gatewayAPI | quote }}
gateway: {{ .Values.gateway.gatewayClass | quote }}
{{- with .Values.publishing.hostnames }}
hostnames:
{{- . | toYaml | nindent 8 }}
{{- end }}
{{- with $kubeRootCa.data }}
kube-root-ca: {{ index . "ca.crt" | b64enc | quote }}
{{- end }}

View file

@ -16,10 +16,11 @@
{{- $kubeovnValues := dict "kube-ovn" $kubeovnDict -}}
{{- $_ := set $networkingComponents "kubeovn" (dict "values" $kubeovnValues) -}}
{{- /* For Talos (isp-full): use KubePrism endpoint and disable cgroup autoMount */ -}}
{{- $ciliumValues := dict "cilium" (dict
{{- $ciliumDict := dict
"k8sServiceHost" "localhost"
"k8sServicePort" "7445"
"cgroup" (dict "autoMount" (dict "enabled" false))) -}}
"cgroup" (dict "autoMount" (dict "enabled" false)) -}}
{{- $ciliumValues := dict "cilium" $ciliumDict -}}
{{- $_ := set $networkingComponents "cilium" (dict "values" $ciliumValues) -}}
{{- end -}}
{{include "cozystack.platform.package" (list "cozystack.networking" "kubeovn-cilium" $ $networkingComponents) }}
@ -80,10 +81,11 @@
{{- $kubeovnValues := dict "kube-ovn" $kubeovnDict -}}
{{- $_ := set $networkingComponents "kubeovn" (dict "values" $kubeovnValues) -}}
{{- /* Cilium configuration - for generic k8s, always enable cgroup autoMount */ -}}
{{- $ciliumValues := dict "cilium" (dict
{{- $ciliumDict := dict
"k8sServiceHost" $apiHost
"k8sServicePort" $apiPort
"cgroup" (dict "autoMount" (dict "enabled" true))) -}}
"cgroup" (dict "autoMount" (dict "enabled" true)) -}}
{{- $ciliumValues := dict "cilium" $ciliumDict -}}
{{- $_ := set $networkingComponents "cilium" (dict "values" $ciliumValues) -}}
{{- end -}}
{{- /* Use kubeovn-cilium-generic variant (no values-talos.yaml) */ -}}
@ -115,7 +117,16 @@
{{- end }}
# Common Packages
{{- $certManagerComponents := dict -}}
{{- if .Values.gateway.gatewayAPI -}}
{{- $certManagerValues := dict "cert-manager" (dict "config" (dict "enableGatewayAPI" true)) -}}
{{- $_ := set $certManagerComponents "cert-manager" (dict "values" $certManagerValues) -}}
{{- end -}}
{{- if gt (len $certManagerComponents) 0 }}
{{include "cozystack.platform.package" (list "cozystack.cert-manager" "default" $ $certManagerComponents) }}
{{- else }}
{{include "cozystack.platform.package.default" (list "cozystack.cert-manager" $) }}
{{- end }}
{{include "cozystack.platform.package.default" (list "cozystack.flux-plunger" $) }}
{{include "cozystack.platform.package.default" (list "cozystack.victoria-metrics-operator" $) }}
{{- $tenantComponents := dict -}}
@ -123,6 +134,9 @@
{{- $_ := set $tenantComponents "tenant" (dict "values" $tenantClusterValues) }}
{{include "cozystack.platform.package" (list "cozystack.tenant-application" "default" $ $tenantComponents) }}
{{include "cozystack.platform.package.default" (list "cozystack.ingress-application" $) }}
{{- if .Values.gateway.gatewayAPI }}
{{include "cozystack.platform.package.default" (list "cozystack.gateway-application" $) }}
{{- end }}
{{include "cozystack.platform.package.default" (list "cozystack.seaweedfs-application" $) }}
{{include "cozystack.platform.package.default" (list "cozystack.info-application" $) }}
{{include "cozystack.platform.package.default" (list "cozystack.monitoring-application" $) }}
@ -145,7 +159,20 @@
# Optional System Packages (controlled via bundles.enabledPackages)
{{include "cozystack.platform.package.optional.default" (list "cozystack.nfs-driver" $) }}
{{include "cozystack.platform.package.optional.default" (list "cozystack.telepresence" $) }}
{{include "cozystack.platform.package.optional.default" (list "cozystack.external-dns" $) }}
{{- $externalDnsComponents := dict -}}
{{- if .Values.gateway.gatewayAPI -}}
{{- $externalDnsValues := dict "external-dns" (dict "sources" (list "service" "ingress" "gateway-httproute" "gateway-tlsroute")) -}}
{{- $_ := set $externalDnsComponents "external-dns" (dict "values" $externalDnsValues) -}}
{{- end -}}
{{- $disabled := default (list) .Values.bundles.disabledPackages -}}
{{- $enabled := default (list) .Values.bundles.enabledPackages -}}
{{- if and (has "cozystack.external-dns" $enabled) (not (has "cozystack.external-dns" $disabled)) -}}
{{- if gt (len $externalDnsComponents) 0 }}
{{include "cozystack.platform.package" (list "cozystack.external-dns" "default" $ $externalDnsComponents) }}
{{- else }}
{{include "cozystack.platform.package.default" (list "cozystack.external-dns" $) }}
{{- end }}
{{- end }}
{{include "cozystack.platform.package.optional.default" (list "cozystack.external-dns-application" $) }}
{{include "cozystack.platform.package.optional.default" (list "cozystack.external-secrets-operator" $) }}
{{include "cozystack.platform.package.optional.default" (list "cozystack.velero" $) }}

View file

@ -34,6 +34,14 @@ networking:
# Set this to comma-separated list of master node IPs to override.
kubeovn:
MASTER_NODES: ""
# Gateway configuration
gateway:
ingress: true
# Enables Gateway API support in cert-manager and external-dns
gatewayAPI: false
# GatewayClass name used by system services (must match the tenant
# that has gateway: true, typically tenant-root)
gatewayClass: tenant-root
# Service publishing and ingress configuration
publishing:
host: "example.org"
@ -43,6 +51,15 @@ publishing:
- dashboard
- vm-exportproxy
- cdi-uploadproxy
# Hostname overrides for system services
# By default, hostname = {service-name}.{host}
# Override individual hostnames when the convention doesn't fit
#
# Example:
# hostnames:
# keycloak: "auth.example.org"
# dashboard: "panel.example.org"
hostnames: {}
apiServerEndpoint: "" # example: "https://api.example.org"
externalIPs: []
certificates:

View file

@ -0,0 +1,77 @@
{{- $gatewayAPI := (index .Values._cluster "gateway-api") | default "false" }}
{{- $clusterIssuer := (index .Values._cluster "issuer-name") | default "letsencrypt-prod" }}
{{- $gateway := .Values._namespace.gateway | default "" }}
{{- $host := .Values._namespace.host }}
{{- $bootboxHost := printf "bootbox.%s" (.Values.host | default $host) }}
{{- $gatewayClassName := .Values._namespace.gateway | default "" }}
{{- if and (eq $gatewayAPI "true") (ne $gateway "") }}
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: bootbox
annotations:
cert-manager.io/cluster-issuer: {{ $clusterIssuer }}
spec:
gatewayClassName: {{ $gatewayClassName }}
infrastructure:
labels:
cozystack.io/gateway: {{ $gateway }}
listeners:
- name: http
protocol: HTTP
port: 80
hostname: {{ $bootboxHost | quote }}
allowedRoutes:
namespaces:
from: Same
- name: https
protocol: HTTPS
port: 443
hostname: {{ $bootboxHost | quote }}
tls:
mode: Terminate
certificateRefs:
- name: bootbox-gateway-tls
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: bootbox-redirect-to-https
spec:
parentRefs:
- name: bootbox
sectionName: http
hostnames:
- {{ $bootboxHost | quote }}
rules:
- filters:
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: bootbox
spec:
parentRefs:
- name: bootbox
sectionName: https
hostnames:
- {{ $bootboxHost | quote }}
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: bootbox
port: 80
{{- end }}

View file

@ -0,0 +1,6 @@
apiVersion: v2
name: gateway
description: Envoy Gateway API controller
icon: /logos/gateway.svg
type: application
version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process

View file

@ -0,0 +1,6 @@
NAME=gateway
include ../../../hack/package.mk
generate:
../../../hack/update-crd.sh

View file

@ -0,0 +1,5 @@
<svg width="144" height="144" viewBox="0 0 144 144" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="144" height="144" rx="24" fill="#6C3FC0"/>
<path d="M72 30L104 72L72 114L40 72L72 30Z" stroke="white" stroke-width="6" stroke-linejoin="round" fill="none"/>
<path d="M72 50L90 72L72 94L54 72L72 50Z" stroke="white" stroke-width="4" stroke-linejoin="round" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 389 B

View file

@ -0,0 +1,115 @@
{{- $exposeGateway := (index .Values._cluster "gateway") | default "" }}
{{- $exposeExternalIPs := (index .Values._cluster "expose-external-ips") | default "" | nospace }}
---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
name: gateway-proxy
spec:
mergeGateways: true
provider:
type: Kubernetes
kubernetes:
envoyDeployment:
replicas: 2
envoyService:
{{- if and (eq $exposeGateway .Release.Namespace) $exposeExternalIPs }}
type: ClusterIP
patch:
type: StrategicMerge
value:
spec:
externalIPs:
{{- range splitList "," $exposeExternalIPs }}
- {{ . }}
{{- end }}
{{- else }}
type: LoadBalancer
{{- end }}
---
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: {{ .Release.Namespace }}
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
parametersRef:
group: gateway.envoyproxy.io
kind: EnvoyProxy
name: gateway-proxy
namespace: {{ .Release.Namespace }}
---
{{/*
HTTP port 80 architecture — two Gateways with mergeGateways:
1. acme-challenge (from: Selector, cozystack.io/system label)
Accepts ACME HTTP-01 challenge HTTPRoutes from system namespaces
(cozy-dashboard, cozy-keycloak, etc.) and per-hostname redirect
HTTPRoutes created by system service charts.
2. http-redirect (from: Same)
Accepts the catch-all redirect-to-https HTTPRoute and ACME
challenges from this tenant namespace (e.g., bucket certificates).
Both Gateways declare port 80 HTTP with no hostname filter. This
relies on Envoy Gateway's mergeGateways to combine them into one
Envoy listener. Standard Gateway API conformance tests may flag this
as a conflict — if a future Envoy Gateway release tightens spec
compliance, consolidation into a single Gateway will be needed
(requires OR logic in label selectors or operator-managed labels).
cert-manager solver references both Gateways as parentRefs. Each
challenge HTTPRoute will have one accepted and one rejected parent —
this is expected and cert-manager handles partial acceptance correctly.
*/}}
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: acme-challenge
spec:
gatewayClassName: {{ .Release.Namespace }}
infrastructure:
labels:
cozystack.io/gateway: {{ .Release.Namespace }}
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
cozystack.io/system: "true"
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: http-redirect
spec:
gatewayClassName: {{ .Release.Namespace }}
infrastructure:
labels:
cozystack.io/gateway: {{ .Release.Namespace }}
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: redirect-to-https
spec:
parentRefs:
- name: http-redirect
sectionName: http
rules:
- filters:
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301

View file

@ -0,0 +1,5 @@
{
"title": "Chart Values",
"type": "object",
"properties": {}
}

View file

@ -0,0 +1,2 @@
_cluster: {}
_namespace: {}

View file

@ -0,0 +1,43 @@
{{- $gatewayAPI := (index .Values._cluster "gateway-api") | default "false" }}
{{- $gateway := .Values._namespace.gateway | default "" }}
{{- $host := .Values._namespace.host }}
{{- $gatewayClassName := .Values._namespace.gateway | default "" }}
{{- if and (eq $gatewayAPI "true") (ne $gateway "") (not (eq .Values.topology "Client")) (.Values.filer.grpcHost) }}
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: seaweedfs-filer
spec:
gatewayClassName: {{ $gatewayClassName }}
infrastructure:
labels:
cozystack.io/gateway: {{ $gateway }}
listeners:
- name: tls-passthrough
protocol: TLS
port: 443
hostname: {{ .Values.filer.grpcHost | default (printf "filer.%s" $host) | quote }}
tls:
mode: Passthrough
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: TLSRoute
metadata:
name: seaweedfs-filer
spec:
parentRefs:
- name: seaweedfs-filer
sectionName: tls-passthrough
hostnames:
- {{ .Values.filer.grpcHost | default (printf "filer.%s" $host) | quote }}
rules:
- backendRefs:
- name: {{ $.Release.Name }}-filer-external
port: 18888
{{- end }}

View file

@ -0,0 +1,52 @@
{{- $gatewayAPI := (index .Values._cluster "gateway-api") | default "false" }}
{{- $clusterIssuer := (index .Values._cluster "issuer-name") | default "letsencrypt-prod" }}
{{- $gateway := .Values._namespace.gateway | default "" }}
{{- $host := .Values._namespace.host }}
{{- $bucketHost := printf "%s.%s" .Values.bucketName $host }}
{{- $gatewayClassName := .Values._namespace.gateway | default "" }}
{{- if and (eq $gatewayAPI "true") (ne $gateway "") }}
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: {{ .Values.bucketName }}-ui
annotations:
cert-manager.io/cluster-issuer: {{ $clusterIssuer }}
spec:
gatewayClassName: {{ $gatewayClassName }}
infrastructure:
labels:
cozystack.io/gateway: {{ $gateway }}
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: {{ $bucketHost | quote }}
tls:
mode: Terminate
certificateRefs:
- name: {{ .Values.bucketName }}-ui-gateway-tls
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: {{ .Values.bucketName }}-ui
spec:
parentRefs:
- name: {{ .Values.bucketName }}-ui
hostnames:
- {{ $bucketHost | quote }}
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: {{ .Values.bucketName }}-ui
port: 8080
{{- end }}

View file

@ -1,56 +1,83 @@
{{- $solver := (index .Values._cluster "solver") | default "http01" }}
{{- $ingressEnabled := (index .Values._cluster "ingress-enabled") | default "true" }}
{{- $gatewayAPI := (index .Values._cluster "gateway-api") | default "false" }}
{{- $gateway := (index .Values._cluster "gateway") | default "" }}
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
privateKeySecretRef:
name: letsencrypt-prod
server: https://acme-v02.api.letsencrypt.org/directory
solvers:
- {{- if eq $solver "dns01" }}
dns01:
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token-secret
key: api-token
{{- else }}
http01:
ingress:
class: nginx
{{- end }}
{{- define "cert-manager-issuers.solvers" -}}
{{- $solver := index . 0 -}}
{{- $ingressEnabled := index . 1 -}}
{{- $gatewayAPI := index . 2 -}}
{{- $gateway := index . 3 }}
solvers:
{{- if eq $solver "dns01" }}
- dns01:
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token-secret
key: api-token
{{- else }}
{{- if eq $ingressEnabled "true" }}
- http01:
ingress:
class: nginx
{{- end }}
{{/* Dual parentRef: acme-challenge accepts system namespaces (Selector),
http-redirect accepts the gateway's own namespace (Same). Each challenge
HTTPRoute will have one accepted and one rejected parent — this is
expected, cert-manager handles partial acceptance correctly.
---
Limitation: Gateway API ACME HTTP-01 only works for the root tenant
and system services. Child tenants with gateway: true have their own
Gateways but the ClusterIssuer always references the root tenant's
Gateways. Child tenant challenges will be rejected by both (no system
label, different namespace). Child tenants should use dns01 solver or
namespace-scoped Issuers. */}}
{{- if and (eq $gatewayAPI "true") (ne $gateway "") }}
- http01:
gatewayHTTPRoute:
parentRefs:
- name: acme-challenge
namespace: {{ $gateway }}
kind: Gateway
- name: http-redirect
namespace: {{ $gateway }}
kind: Gateway
{{- end }}
{{- if and (ne $ingressEnabled "true") (or (ne $gatewayAPI "true") (eq $gateway "")) }}
{{- fail "At least one of gateway.ingress or gateway.gatewayAPI must be enabled for ACME HTTP-01 solver" }}
{{- end }}
{{- end }}
{{- end -}}
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-stage
spec:
acme:
privateKeySecretRef:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
privateKeySecretRef:
name: letsencrypt-prod
server: https://acme-v02.api.letsencrypt.org/directory
{{ include "cert-manager-issuers.solvers" (list $solver $ingressEnabled $gatewayAPI $gateway) | indent 4 }}
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-stage
spec:
acme:
privateKeySecretRef:
name: letsencrypt-stage
server: https://acme-staging-v02.api.letsencrypt.org/directory
solvers:
- {{- if eq $solver "dns01" }}
dns01:
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token-secret
key: api-token
{{- else }}
http01:
ingress:
class: nginx
{{- end }}
server: https://acme-staging-v02.api.letsencrypt.org/directory
{{ include "cert-manager-issuers.solvers" (list $solver $ingressEnabled $gatewayAPI $gateway) | indent 4 }}
---
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned-cluster-issuer
spec:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned-cluster-issuer
spec:
selfSigned: {}

View file

@ -0,0 +1 @@
cert-manager: {}

View file

@ -0,0 +1,50 @@
{{- $gatewayAPI := (index .Values._cluster "gateway-api") | default "false" }}
{{- $host := index .Values._cluster "root-host" }}
{{- $hostnames := .Values._cluster.hostnames | default dict }}
{{- $exposeServices := splitList "," ((index .Values._cluster "expose-services") | default "") }}
{{- $gateway := ((.Values._namespace).gateway) | default (index .Values._cluster "gateway") | default "" }}
{{- $gatewayClassName := $gateway }}
{{- $apiHost := index $hostnames "api" | default (printf "api.%s" $host) }}
{{- if and (eq $gatewayAPI "true") (has "api" $exposeServices) }}
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: kubernetes-api
namespace: default
spec:
gatewayClassName: {{ $gatewayClassName }}
{{- if $gateway }}
infrastructure:
labels:
cozystack.io/gateway: {{ $gateway }}
{{- end }}
listeners:
- name: tls-passthrough
protocol: TLS
port: 443
hostname: {{ $apiHost | quote }}
tls:
mode: Passthrough
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: TLSRoute
metadata:
name: kubernetes-api
namespace: default
spec:
parentRefs:
- name: kubernetes-api
sectionName: tls-passthrough
hostnames:
- {{ $apiHost | quote }}
rules:
- backendRefs:
- name: kubernetes
port: 443
{{- end }}

View file

@ -16,5 +16,6 @@ stringData:
host: {{ index .Values._cluster "root-host" | quote }}
etcd: tenant-root
ingress: tenant-root
gateway: tenant-root
monitoring: tenant-root
seaweedfs: tenant-root

View file

@ -0,0 +1,74 @@
{{- $gatewayAPI := (index .Values._cluster "gateway-api") | default "false" }}
{{- $clusterIssuer := (index .Values._cluster "issuer-name") | default "letsencrypt-prod" }}
{{- $host := index .Values._cluster "root-host" }}
{{- $hostnames := .Values._cluster.hostnames | default dict }}
{{- $exposeServices := splitList "," ((index .Values._cluster "expose-services") | default "") }}
{{- $gateway := ((.Values._namespace).gateway) | default (index .Values._cluster "gateway") | default "" }}
{{- $gatewayClassName := $gateway }}
{{- $dashboardHost := index $hostnames "dashboard" | default (printf "dashboard.%s" $host) }}
{{- if and (eq $gatewayAPI "true") (has "dashboard" $exposeServices) }}
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: dashboard
annotations:
cert-manager.io/cluster-issuer: {{ $clusterIssuer }}
spec:
gatewayClassName: {{ $gatewayClassName }}
{{- if $gateway }}
infrastructure:
labels:
cozystack.io/gateway: {{ $gateway }}
{{- end }}
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: {{ $dashboardHost | quote }}
tls:
mode: Terminate
certificateRefs:
- name: dashboard-gateway-tls
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: dashboard-redirect-to-https
spec:
parentRefs:
- name: acme-challenge
namespace: {{ $gateway }}
sectionName: http
hostnames:
- {{ $dashboardHost | quote }}
rules:
- filters:
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: dashboard-web
spec:
parentRefs:
- name: dashboard
hostnames:
- {{ $dashboardHost | quote }}
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: incloud-web-gatekeeper
port: 8000
{{- end }}

View file

@ -0,0 +1,3 @@
apiVersion: v2
name: cozy-envoy-gateway
version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process

View file

@ -0,0 +1,17 @@
export NAME=envoy-gateway
export NAMESPACE=cozy-$(NAME)
CHART_VERSION=v1.7.1
include ../../../hack/package.mk
update:
rm -rf charts
helm pull oci://docker.io/envoyproxy/gateway-helm --version $(CHART_VERSION) --untar --untardir charts
rm -rf templates/crds
mkdir -p templates/crds
cp charts/gateway-helm/crds/generated/*.yaml templates/crds/
rm -rf charts/gateway-helm/crds
sed -i'' -e 's/value: {{ .Values.kubernetesClusterDomain }}/value: {{ .Values.global.clusterDomain | default .Values.kubernetesClusterDomain }}/' \
charts/gateway-helm/templates/envoy-gateway-deployment.yaml \
charts/gateway-helm/templates/certgen.yaml

View file

@ -0,0 +1,26 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/
# Template files
*.tmpl.*

View file

@ -0,0 +1,20 @@
apiVersion: v2
appVersion: v1.7.1
description: The Helm chart for Envoy Gateway
home: https://gateway.envoyproxy.io/
icon: https://raw.githubusercontent.com/envoyproxy/gateway/main/site/assets/icons/logo.svg
keywords:
- gateway-api
- envoyproxy
- envoy-gateway
- eg
maintainers:
- name: envoy-gateway-steering-committee
url: https://github.com/envoyproxy/gateway/blob/main/GOVERNANCE.md
- name: envoy-gateway-maintainers
url: https://github.com/envoyproxy/gateway/blob/main/CODEOWNERS
name: gateway-helm
sources:
- https://github.com/envoyproxy/gateway
type: application
version: v1.7.1

View file

@ -0,0 +1,121 @@
# gateway-helm
![Version: v0.0.0-latest](https://img.shields.io/badge/Version-v0.0.0--latest-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: latest](https://img.shields.io/badge/AppVersion-latest-informational?style=flat-square)
The Helm chart for Envoy Gateway
**Homepage:** <https://gateway.envoyproxy.io/>
## Maintainers
| Name | Email | Url |
| ---- | ------ | --- |
| envoy-gateway-steering-committee | | <https://github.com/envoyproxy/gateway/blob/main/GOVERNANCE.md> |
| envoy-gateway-maintainers | | <https://github.com/envoyproxy/gateway/blob/main/CODEOWNERS> |
## Source Code
* <https://github.com/envoyproxy/gateway>
## Usage
[Helm](https://helm.sh) must be installed to use the charts.
Please refer to Helm's [documentation](https://helm.sh/docs) to get started.
### Install from DockerHub
Once Helm has been set up correctly, install the chart from dockerhub:
``` shell
helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace
```
You can find all helm chart release in [Dockerhub](https://hub.docker.com/r/envoyproxy/gateway-helm/tags)
### Install from Source Code
You can also install the helm chart from the source code:
To install the eg chart along with Gateway API CRDs and Envoy Gateway CRDs:
``` shell
make kube-deploy TAG=latest
```
### Skip install CRDs
You can install the eg chart along without Gateway API CRDs and Envoy Gateway CRDs, make sure CRDs exist in Cluster first if you want to skip to install them, otherwise EG may fail to start:
``` shell
helm install eg --create-namespace oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --skip-crds
```
To uninstall the chart:
``` shell
helm uninstall eg -n envoy-gateway-system
```
## Values
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| certgen | object | `{"job":{"affinity":{},"annotations":{},"args":[],"nodeSelector":{},"pod":{"annotations":{},"labels":{}},"resources":{},"securityContext":{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"privileged":false,"readOnlyRootFilesystem":true,"runAsGroup":65532,"runAsNonRoot":true,"runAsUser":65532,"seccompProfile":{"type":"RuntimeDefault"}},"tolerations":[],"ttlSecondsAfterFinished":30},"rbac":{"annotations":{},"labels":{}}}` | Certgen is used to generate the certificates required by EnvoyGateway. If you want to construct a custom certificate, you can generate a custom certificate through Cert-Manager before installing EnvoyGateway. Certgen will not overwrite the custom certificate. Please do not manually modify `values.yaml` to disable certgen, it may cause EnvoyGateway OIDC,OAuth2,etc. to not work as expected. |
| config.envoyGateway | object | `{"extensionApis":{},"gateway":{"controllerName":"gateway.envoyproxy.io/gatewayclass-controller"},"logging":{"level":{"default":"info"}},"provider":{"type":"Kubernetes"}}` | EnvoyGateway configuration. Visit https://gateway.envoyproxy.io/docs/api/extension_types/#envoygateway to view all options. |
| createNamespace | bool | `false` | |
| deployment.annotations | object | `{}` | |
| deployment.envoyGateway.image.repository | string | `""` | |
| deployment.envoyGateway.image.tag | string | `""` | |
| deployment.envoyGateway.imagePullPolicy | string | `""` | |
| deployment.envoyGateway.imagePullSecrets | list | `[]` | |
| deployment.envoyGateway.resources.limits.memory | string | `"1024Mi"` | |
| deployment.envoyGateway.resources.requests.cpu | string | `"100m"` | |
| deployment.envoyGateway.resources.requests.memory | string | `"256Mi"` | |
| deployment.envoyGateway.securityContext.allowPrivilegeEscalation | bool | `false` | |
| deployment.envoyGateway.securityContext.capabilities.drop[0] | string | `"ALL"` | |
| deployment.envoyGateway.securityContext.privileged | bool | `false` | |
| deployment.envoyGateway.securityContext.runAsGroup | int | `65532` | |
| deployment.envoyGateway.securityContext.runAsNonRoot | bool | `true` | |
| deployment.envoyGateway.securityContext.runAsUser | int | `65532` | |
| deployment.envoyGateway.securityContext.seccompProfile.type | string | `"RuntimeDefault"` | |
| deployment.pod.affinity | object | `{}` | |
| deployment.pod.annotations."prometheus.io/port" | string | `"19001"` | |
| deployment.pod.annotations."prometheus.io/scrape" | string | `"true"` | |
| deployment.pod.labels | object | `{}` | |
| deployment.pod.nodeSelector | object | `{}` | |
| deployment.pod.tolerations | list | `[]` | |
| deployment.pod.topologySpreadConstraints | list | `[]` | |
| deployment.ports[0].name | string | `"grpc"` | |
| deployment.ports[0].port | int | `18000` | |
| deployment.ports[0].targetPort | int | `18000` | |
| deployment.ports[1].name | string | `"ratelimit"` | |
| deployment.ports[1].port | int | `18001` | |
| deployment.ports[1].targetPort | int | `18001` | |
| deployment.ports[2].name | string | `"wasm"` | |
| deployment.ports[2].port | int | `18002` | |
| deployment.ports[2].targetPort | int | `18002` | |
| deployment.ports[3].name | string | `"metrics"` | |
| deployment.ports[3].port | int | `19001` | |
| deployment.ports[3].targetPort | int | `19001` | |
| deployment.priorityClassName | string | `nil` | |
| deployment.replicas | int | `1` | |
| global.imagePullSecrets | list | `[]` | Global override for image pull secrets |
| global.imageRegistry | string | `""` | Global override for image registry |
| global.images.envoyGateway.image | string | `nil` | |
| global.images.envoyGateway.pullPolicy | string | `nil` | |
| global.images.envoyGateway.pullSecrets | list | `[]` | |
| global.images.ratelimit.image | string | `"docker.io/envoyproxy/ratelimit:c8765e89"` | |
| global.images.ratelimit.pullPolicy | string | `"IfNotPresent"` | |
| global.images.ratelimit.pullSecrets | list | `[]` | |
| hpa.behavior | object | `{}` | |
| hpa.enabled | bool | `false` | |
| hpa.maxReplicas | int | `1` | |
| hpa.metrics | list | `[]` | |
| hpa.minReplicas | int | `1` | |
| kubernetesClusterDomain | string | `"cluster.local"` | |
| podDisruptionBudget.minAvailable | int | `0` | |
| service.annotations | object | `{}` | |
| service.trafficDistribution | string | `""` | |
| service.type | string | `"ClusterIP"` | Service type. Can be set to LoadBalancer with specific IP, e.g.: type: LoadBalancer loadBalancerIP: 10.236.90.20 |
| topologyInjector.annotations | object | `{}` | |
| topologyInjector.enabled | bool | `true` | |

View file

@ -0,0 +1,20 @@
**************************************************************************
*** PLEASE BE PATIENT: Envoy Gateway may take a few minutes to install ***
**************************************************************************
Envoy Gateway is an open source project for managing Envoy Proxy as a standalone or Kubernetes-based application gateway.
Thank you for installing Envoy Gateway! 🎉
Your release is named: {{ .Release.Name }}. 🎉
Your release is in namespace: {{ .Release.Namespace }}. 🎉
To learn more about the release, try:
$ helm status {{ .Release.Name }} -n {{ .Release.Namespace }}
$ helm get all {{ .Release.Name }} -n {{ .Release.Namespace }}
To have a quickstart of Envoy Gateway, please refer to https://gateway.envoyproxy.io/latest/tasks/quickstart.
To get more details, please visit https://gateway.envoyproxy.io and https://github.com/envoyproxy/gateway.

View file

@ -0,0 +1,187 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "eg.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "eg.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "eg.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "eg.labels" -}}
helm.sh/chart: {{ include "eg.chart" . }}
{{ include "eg.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "eg.selectorLabels" -}}
app.kubernetes.io/name: {{ include "eg.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "eg.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "eg.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
The name of the Envoy Gateway image.
*/}}
{{- define "eg.image" -}}
{{/* if deployment-specific repository is defined, it takes precedence */}}
{{- if .Values.deployment.envoyGateway.image.repository -}}
{{/* if global.imageRegistry is defined, it takes precedence always */}}
{{- if .Values.global.imageRegistry -}}
{{- $repositoryParts := splitn "/" 2 .Values.deployment.envoyGateway.image.repository -}}
{{- $registryName := .Values.global.imageRegistry -}}
{{- $repositoryName := $repositoryParts._1 -}}
{{- $imageTag := default .Chart.AppVersion .Values.deployment.envoyGateway.image.tag -}}
{{- printf "%s/%s:%s" $registryName $repositoryName $imageTag -}}
{{/* if global.imageRegistry is undefined, take repository as is */}}
{{- else -}}
{{- $imageTag := default .Chart.AppVersion .Values.deployment.envoyGateway.image.tag -}}
{{- printf "%s:%s" .Values.deployment.envoyGateway.image.repository $imageTag -}}
{{- end -}}
{{/* else, global image is used if defined */}}
{{- else if .Values.global.images.envoyGateway.image -}}
{{- $imageParts := splitn "/" 2 .Values.global.images.envoyGateway.image -}}
{{/* if global.imageRegistry is defined, it takes precedence always */}}
{{- $registryName := default $imageParts._0 .Values.global.imageRegistry -}}
{{- $repositoryTag := $imageParts._1 -}}
{{- $repositoryParts := splitn ":" 2 $repositoryTag -}}
{{- $repositoryName := $repositoryParts._0 -}}
{{- $imageTag := $repositoryParts._1 -}}
{{- printf "%s/%s:%s" $registryName $repositoryName $imageTag -}}
{{- else -}}
docker.io/envoyproxy/gateway:{{ .Chart.Version }}
{{- end -}}
{{- end -}}
{{/*
Pull policy for the Envoy Gateway image.
*/}}
{{- define "eg.image.pullPolicy" -}}
{{- default .Values.deployment.envoyGateway.imagePullPolicy .Values.global.images.envoyGateway.pullPolicy -}}
{{- end }}
{{/*
Pull secrets for the Envoy Gateway image.
*/}}
{{- define "eg.image.pullSecrets" -}}
{{- if .Values.global.imagePullSecrets -}}
imagePullSecrets:
{{ toYaml .Values.global.imagePullSecrets }}
{{- else if .Values.deployment.envoyGateway.imagePullSecrets -}}
imagePullSecrets:
{{ toYaml .Values.deployment.envoyGateway.imagePullSecrets }}
{{- else if .Values.global.images.envoyGateway.pullSecrets -}}
imagePullSecrets:
{{ toYaml .Values.global.images.envoyGateway.pullSecrets }}
{{- else -}}
imagePullSecrets: {{ toYaml list }}
{{- end }}
{{- end }}
{{/*
The name of the Envoy Ratelimit image.
*/}}
{{- define "eg.ratelimit.image" -}}
{{- $imageParts := splitn "/" 2 .Values.global.images.ratelimit.image -}}
{{/* if global.imageRegistry is defined, it takes precedence always */}}
{{- $registryName := default $imageParts._0 .Values.global.imageRegistry -}}
{{- $repositoryTag := $imageParts._1 -}}
{{- $repositoryParts := splitn ":" 2 $repositoryTag -}}
{{- $repositoryName := $repositoryParts._0 -}}
{{- $imageTag := default "master" $repositoryParts._1 -}}
{{- printf "%s/%s:%s" $registryName $repositoryName $imageTag -}}
{{- end -}}
{{/*
Pull secrets for the Envoy Ratelimit image.
*/}}
{{- define "eg.ratelimit.image.pullSecrets" -}}
{{- if .Values.global.imagePullSecrets }}
imagePullSecrets:
{{ toYaml .Values.global.imagePullSecrets }}
{{- else if .Values.global.images.ratelimit.pullSecrets -}}
imagePullSecrets:
{{ toYaml .Values.global.images.ratelimit.pullSecrets }}
{{- else }}
imagePullSecrets: {{ toYaml list }}
{{- end }}
{{- end }}
{{/*
The default Envoy Gateway configuration.
*/}}
{{- define "eg.default-envoy-gateway-config" -}}
provider:
type: Kubernetes
kubernetes:
rateLimitDeployment:
container:
image: {{ include "eg.ratelimit.image" . }}
{{- if (or .Values.global.imagePullSecrets .Values.global.images.ratelimit.pullSecrets) }}
pod:
{{- include "eg.ratelimit.image.pullSecrets" . | nindent 8 }}
{{- end }}
{{- with .Values.global.images.ratelimit.pullPolicy }}
patch:
type: StrategicMerge
value:
spec:
template:
spec:
containers:
- name: envoy-ratelimit
imagePullPolicy: {{ . }}
{{- end }}
shutdownManager:
image: {{ include "eg.image" . }}
{{- with .Values.config.envoyGateway.extensionApis }}
extensionApis:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- if not .Values.topologyInjector.enabled }}
proxyTopologyInjector:
disabled: true
{{- end }}
{{- end }}

View file

@ -0,0 +1,273 @@
{{/*
All namespaced resources for Envoy Gateway RBAC.
*/}}
{{- define "eg.rbac.namespaced" -}}
- {{ include "eg.rbac.namespaced.basic" . | nindent 2 | trim }}
- {{ include "eg.rbac.namespaced.apps" . | nindent 2 | trim }}
- {{ include "eg.rbac.namespaced.discovery" . | nindent 2 | trim }}
- {{ include "eg.rbac.namespaced.gateway.envoyproxy" . | nindent 2 | trim }}
- {{ include "eg.rbac.namespaced.gateway.envoyproxy.status" . | nindent 2 | trim }}
- {{ include "eg.rbac.namespaced.gateway.networking" . | nindent 2 | trim }}
- {{ include "eg.rbac.namespaced.gateway.networking.status" . | nindent 2 | trim }}
- {{ include "eg.rbac.namespaced.gateway.networking.experimental" . | nindent 2 | trim }}
- {{ include "eg.rbac.namespaced.gateway.networking.experimental.status" . | nindent 2 | trim }}
{{- if .Values.topologyInjector.enabled }}
- {{ include "eg.rbac.namespaced.topologyinjector" . | nindent 2 | trim }}
{{- end }}
{{- end }}
{{/*
All cluster scoped resources for Envoy Gateway RBAC.
*/}}
{{- define "eg.rbac.cluster" -}}
- {{ include "eg.rbac.cluster.basic" . | nindent 2 | trim }}
- {{ include "eg.rbac.cluster.gateway.networking" . | nindent 2 | trim }}
- {{ include "eg.rbac.cluster.gateway.networking.status" . | nindent 2 | trim }}
- {{ include "eg.rbac.cluster.multiclusterservices" . | nindent 2 | trim }}
{{- end }}
{{/*
Namespaced
*/}}
{{- define "eg.rbac.namespaced.basic" -}}
apiGroups:
- ""
resources:
- configmaps
- secrets
- services
verbs:
- get
- list
- watch
{{- end }}
{{- define "eg.rbac.namespaced.topologyinjector" -}}
apiGroups:
- ""
resources:
- pods
- pods/binding
verbs:
- get
- list
- patch
- update
- watch
{{- end }}
{{- define "eg.rbac.namespaced.apps" -}}
apiGroups:
- apps
resources:
- deployments
- daemonsets
verbs:
- get
- list
- watch
{{- end }}
{{- define "eg.rbac.namespaced.discovery" -}}
apiGroups:
- discovery.k8s.io
resources:
- endpointslices
verbs:
- get
- list
- watch
{{- end }}
{{- define "eg.rbac.namespaced.gateway.envoyproxy" -}}
apiGroups:
- gateway.envoyproxy.io
resources:
- envoyproxies
- envoypatchpolicies
- clienttrafficpolicies
- backendtrafficpolicies
- securitypolicies
- envoyextensionpolicies
- backends
- httproutefilters
verbs:
- get
- list
- watch
{{- end }}
{{- define "eg.rbac.namespaced.gateway.envoyproxy.status" -}}
apiGroups:
- gateway.envoyproxy.io
resources:
- envoypatchpolicies/status
- clienttrafficpolicies/status
- backendtrafficpolicies/status
- securitypolicies/status
- envoyextensionpolicies/status
- backends/status
verbs:
- update
{{- end }}
{{- define "eg.rbac.namespaced.gateway.networking" -}}
apiGroups:
- gateway.networking.k8s.io
resources:
- gateways
- grpcroutes
- httproutes
- referencegrants
- tcproutes
- tlsroutes
- udproutes
- backendtlspolicies
verbs:
- get
- list
- watch
{{- end }}
{{- define "eg.rbac.namespaced.gateway.networking.status" -}}
apiGroups:
- gateway.networking.k8s.io
resources:
- gateways/status
- grpcroutes/status
- httproutes/status
- tcproutes/status
- tlsroutes/status
- udproutes/status
- backendtlspolicies/status
verbs:
- update
{{- end }}
{{- define "eg.rbac.namespaced.gateway.networking.experimental" -}}
apiGroups:
- gateway.networking.x-k8s.io
resources:
- xlistenersets
verbs:
- get
- list
- watch
{{- end }}
{{- define "eg.rbac.namespaced.gateway.networking.experimental.status" -}}
apiGroups:
- gateway.networking.x-k8s.io
resources:
- xlistenersets/status
verbs:
- update
{{- end }}
{{/*
Cluster scope
*/}}
{{- define "eg.rbac.cluster.basic" -}}
apiGroups:
- ""
resources:
- nodes
- namespaces
verbs:
- get
- list
- watch
{{- end }}
{{- define "eg.rbac.cluster.gateway.networking" -}}
apiGroups:
- gateway.networking.k8s.io
resources:
- gatewayclasses
verbs:
- get
- list
- patch
- update
- watch
{{- end }}
{{- define "eg.rbac.cluster.multiclusterservices" -}}
apiGroups:
- multicluster.x-k8s.io
resources:
- serviceimports
verbs:
- get
- list
- watch
{{- end }}
{{- define "eg.rbac.cluster.gateway.networking.status" -}}
apiGroups:
- gateway.networking.k8s.io
resources:
- gatewayclasses/status
verbs:
- update
{{- end }}
{{- define "eg.rbac.infra.basic" -}}
- apiGroups:
- ""
resources:
- serviceaccounts
- services
- configmaps
verbs:
- create
- get
- list
- delete
- deletecollection
- patch
- apiGroups:
- apps
resources:
- deployments
- daemonsets
verbs:
- create
- get
- delete
- deletecollection
- patch
- apiGroups:
- autoscaling
- policy
resources:
- horizontalpodautoscalers
- poddisruptionbudgets
verbs:
- create
- get
- list
- delete
- deletecollection
- patch
- apiGroups:
- certificates.k8s.io
resources:
- clustertrustbundles
verbs:
- list
- get
- watch
{{- end }}
{{- define "eg.rbac.infra.tokenreview" -}}
- apiGroups:
- authentication.k8s.io
resources:
- tokenreviews
verbs:
- create
{{- end }}

View file

@ -0,0 +1,127 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "eg.fullname" . }}-certgen
namespace: '{{ .Release.Namespace }}'
labels:
{{- include "eg.labels" . | nindent 4 }}
{{- if .Values.certgen.rbac.labels }}
{{- toYaml .Values.certgen.rbac.labels | nindent 4 }}
{{- end }}
annotations:
"helm.sh/hook": pre-install, pre-upgrade
"helm.sh/hook-weight": "-1" # Ensure rbac is created before the certgen job when using ArgoCD.
{{- if .Values.certgen.rbac.annotations }}
{{- toYaml .Values.certgen.rbac.annotations | nindent 4 -}}
{{- end }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ include "eg.fullname" . }}-certgen
namespace: '{{ .Release.Namespace }}'
labels:
{{- include "eg.labels" . | nindent 4 }}
{{- if .Values.certgen.rbac.labels }}
{{- toYaml .Values.certgen.rbac.labels | nindent 4 }}
{{- end }}
annotations:
"helm.sh/hook": pre-install, pre-upgrade
"helm.sh/hook-weight": "-1" # Ensure rbac is created before the certgen job when using ArgoCD.
{{- if .Values.certgen.rbac.annotations }}
{{- toYaml .Values.certgen.rbac.annotations | nindent 4 -}}
{{- end }}
rules:
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- create
- update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ include "eg.fullname" . }}-certgen
namespace: '{{ .Release.Namespace }}'
labels:
{{- include "eg.labels" . | nindent 4 }}
{{- if .Values.certgen.rbac.labels }}
{{- toYaml .Values.certgen.rbac.labels | nindent 4 }}
{{- end }}
annotations:
"helm.sh/hook": pre-install, pre-upgrade
"helm.sh/hook-weight": "-1" # Ensure rbac is created before the certgen job when using ArgoCD.
{{- if .Values.certgen.rbac.annotations }}
{{- toYaml .Values.certgen.rbac.annotations | nindent 4 -}}
{{- end }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: '{{ include "eg.fullname" . }}-certgen'
subjects:
- kind: ServiceAccount
name: '{{ include "eg.fullname" . }}-certgen'
namespace: '{{ .Release.Namespace }}'
---
{{- if .Values.topologyInjector.enabled }}
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: '{{ include "eg.fullname" . }}-certgen:{{ .Release.Namespace }}'
labels:
{{- include "eg.labels" . | nindent 4 }}
{{- if .Values.certgen.rbac.labels }}
{{- toYaml .Values.certgen.rbac.labels | nindent 4 }}
{{- end }}
annotations:
"helm.sh/hook": pre-install, pre-upgrade
"helm.sh/hook-weight": "-1" # Ensure rbac is created before the certgen job when using ArgoCD.
{{- if .Values.certgen.rbac.annotations }}
{{- toYaml .Values.certgen.rbac.annotations | nindent 4 -}}
{{- end }}
rules:
- apiGroups:
- admissionregistration.k8s.io
resources:
- mutatingwebhookconfigurations
verbs:
- get
- list
- watch
- apiGroups:
- admissionregistration.k8s.io
resources:
- mutatingwebhookconfigurations
resourceNames:
- 'envoy-gateway-topology-injector.{{ .Release.Namespace }}'
verbs:
- update
- patch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: '{{ include "eg.fullname" . }}-certgen:{{ .Release.Namespace }}'
labels:
{{- include "eg.labels" . | nindent 4 }}
{{- if .Values.certgen.rbac.labels }}
{{- toYaml .Values.certgen.rbac.labels | nindent 4 }}
{{- end }}
annotations:
"helm.sh/hook": pre-install, pre-upgrade
"helm.sh/hook-weight": "-1" # Ensure rbac is created before the certgen job when using ArgoCD.
{{- if .Values.certgen.rbac.annotations }}
{{- toYaml .Values.certgen.rbac.annotations | nindent 4 -}}
{{- end }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: '{{ include "eg.fullname" . }}-certgen:{{ .Release.Namespace }}'
subjects:
- kind: ServiceAccount
name: '{{ include "eg.fullname" . }}-certgen'
namespace: '{{ .Release.Namespace }}'
{{- end }}

View file

@ -0,0 +1,79 @@
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "eg.fullname" . }}-certgen
namespace: '{{ .Release.Namespace }}'
labels:
{{- include "eg.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": pre-install, pre-upgrade
{{- if .Values.certgen.job.annotations }}
{{- toYaml .Values.certgen.job.annotations | nindent 4 -}}
{{- end }}
spec:
backoffLimit: 1
completions: 1
parallelism: 1
template:
metadata:
labels:
app: certgen
{{- if .Values.certgen.job.pod.labels }}
{{- toYaml .Values.certgen.job.pod.labels | nindent 8 -}}
{{- end }}
{{- if .Values.certgen.job.pod.annotations }}
annotations:
{{- toYaml .Values.certgen.job.pod.annotations | nindent 8 -}}
{{- end }}
spec:
containers:
{{- $args := .Values.certgen.job.args }}
{{- if not .Values.topologyInjector.enabled }}
{{- $args = append $args "--disable-topology-injector" }}
{{- end }}
{{- if $args }}
- args:
{{- toYaml $args | nindent 8 }}
command:
- envoy-gateway
- certgen
{{- else }}
- command:
- envoy-gateway
- certgen
{{- end }}
env:
- name: ENVOY_GATEWAY_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: KUBERNETES_CLUSTER_DOMAIN
value: {{ .Values.global.clusterDomain | default .Values.kubernetesClusterDomain }}
image: {{ include "eg.image" . }}
imagePullPolicy: {{ include "eg.image.pullPolicy" . }}
name: envoy-gateway-certgen
{{- with .Values.certgen.job.resources }}
resources:
{{- toYaml . | nindent 10 }}
{{- end }}
securityContext:
{{- toYaml .Values.certgen.job.securityContext | nindent 10 }}
{{- include "eg.image.pullSecrets" . | nindent 6 }}
{{- with .Values.certgen.job.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.certgen.job.nodeSelector }}
nodeSelector:
{{ toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.certgen.job.tolerations }}
tolerations:
{{- toYaml . | nindent 6 }}
{{- end }}
restartPolicy: Never
serviceAccountName: {{ include "eg.fullname" . }}-certgen
{{- if not ( kindIs "invalid" .Values.certgen.job.ttlSecondsAfterFinished) }}
ttlSecondsAfterFinished: {{ .Values.certgen.job.ttlSecondsAfterFinished }}
{{- end }}

View file

@ -0,0 +1,15 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: envoy-gateway-config
namespace: '{{ .Release.Namespace }}'
labels:
{{- include "eg.labels" . | nindent 4 }}
data:
envoy-gateway.yaml: |
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyGateway
{{- $baseEnvoyGatewayConfig := include "eg.default-envoy-gateway-config" . | fromYaml }}
{{- $userEnvoyGatewayConfig := .Values.config.envoyGateway }}
{{- $mergedEnvoyGatewayConfig := merge $userEnvoyGatewayConfig $baseEnvoyGatewayConfig }}
{{- toYaml $mergedEnvoyGatewayConfig | nindent 4 }}

View file

@ -0,0 +1,110 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: envoy-gateway
namespace: '{{ .Release.Namespace }}'
{{- with .Values.deployment.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
labels:
control-plane: envoy-gateway
{{- include "eg.labels" . | nindent 4 }}
spec:
{{- if not .Values.hpa.enabled }}
replicas: {{ .Values.deployment.replicas }}
{{- end }}
selector:
matchLabels:
control-plane: envoy-gateway
{{- include "eg.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.deployment.pod.annotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
control-plane: envoy-gateway
{{- include "eg.selectorLabels" . | nindent 8 }}
{{- with .Values.deployment.pod.labels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.deployment.pod.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.deployment.pod.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.deployment.pod.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 6 }}
{{- end }}
{{- with .Values.deployment.pod.tolerations }}
tolerations:
{{- toYaml . | nindent 6 }}
{{- end }}
containers:
- args:
- server
- --config-path=/config/envoy-gateway.yaml
env:
- name: ENVOY_GATEWAY_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: KUBERNETES_CLUSTER_DOMAIN
value: {{ .Values.global.clusterDomain | default .Values.kubernetesClusterDomain }}
image: {{ include "eg.image" . }}
imagePullPolicy: {{ include "eg.image.pullPolicy" . }}
livenessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 15
periodSeconds: 20
name: envoy-gateway
ports:
{{- range .Values.deployment.ports }}
- containerPort: {{ .port }}
name: {{ .name }}
{{- end}}
{{- if .Values.topologyInjector.enabled }}
- name: webhook
containerPort: 9443
{{- end }}
readinessProbe:
httpGet:
path: /readyz
port: 8081
initialDelaySeconds: 5
periodSeconds: 10
resources:
{{- toYaml .Values.deployment.envoyGateway.resources | nindent 10 }}
securityContext:
{{- toYaml .Values.deployment.envoyGateway.securityContext | nindent 10 }}
volumeMounts:
- mountPath: /config
name: envoy-gateway-config
readOnly: true
- mountPath: /certs
name: certs
readOnly: true
{{- include "eg.image.pullSecrets" . | nindent 6 }}
{{- with .Values.deployment.priorityClassName }}
priorityClassName: {{ . | quote }}
{{- end }}
serviceAccountName: envoy-gateway
terminationGracePeriodSeconds: 10
volumes:
- configMap:
defaultMode: 420
name: envoy-gateway-config
name: envoy-gateway-config
- name: certs
secret:
secretName: envoy-gateway

View file

@ -0,0 +1,24 @@
{{- if .Values.hpa.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: envoy-gateway
namespace: '{{ $.Release.Namespace }}'
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: envoy-gateway
{{- if .Values.hpa.minReplicas }}
minReplicas: {{ .Values.hpa.minReplicas }}
{{- end }}
maxReplicas: {{ required ".Values.hps.maxReplicas is required when hpa is enabled" .Values.hpa.maxReplicas }}
{{- if .Values.hpa.behavior }}
behavior:
{{ toYaml .Values.hpa.behavior | indent 4 }}
{{- end }}
{{- if .Values.hpa.metrics }}
metrics:
{{ toYaml .Values.hpa.metrics | indent 4 }}
{{- end }}
{{- end }}

View file

@ -0,0 +1,18 @@
{{- if or (and .Values.podDisruptionBudget.minAvailable (ge (int .Values.podDisruptionBudget.minAvailable) 1) ) (and .Values.podDisruptionBudget.maxUnavailable (ge (int .Values.podDisruptionBudget.maxUnavailable) 1) )}}
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: envoy-gateway
namespace: '{{ .Release.Namespace }}'
spec:
{{- if and .Values.podDisruptionBudget.minAvailable }}
minAvailable: {{ .Values.podDisruptionBudget.minAvailable }}
{{- end }}
{{- if .Values.podDisruptionBudget.maxUnavailable }}
maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }}
{{- end }}
selector:
matchLabels:
control-plane: envoy-gateway
{{- include "eg.selectorLabels" . | nindent 6 }}
{{- end }}

View file

@ -0,0 +1,82 @@
{{ $watchedNamespaces := list }}
{{ if .Values.config.envoyGateway.provider.kubernetes }}
{{ $kube := .Values.config.envoyGateway.provider.kubernetes }}
{{ if $kube.watch }}
{{ if $kube.watch.namespaces }}
{{ if gt (len $kube.watch.namespaces) 0 }}
{{ $watchedNamespaces = $kube.watch.namespaces }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{ if gt (len $watchedNamespaces) 0 }}
{{ range $_, $ns := $watchedNamespaces }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
creationTimestamp: null
name: {{ include "eg.fullname" $ }}-envoy-gateway-role
namespace: {{ $ns | quote }}
rules:
{{ include "eg.rbac.namespaced" $ }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ include "eg.fullname" $ }}-envoy-gateway-rolebinding
namespace: {{ $ns | quote }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ include "eg.fullname" $ }}-envoy-gateway-role
subjects:
- kind: ServiceAccount
name: 'envoy-gateway'
namespace: '{{ $.Release.Namespace }}'
{{ end }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: {{ include "eg.fullname" . }}-envoy-gateway-role
rules:
{{ include "eg.rbac.cluster" . }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ include "eg.fullname" . }}-envoy-gateway-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ include "eg.fullname" . }}-envoy-gateway-role
subjects:
- kind: ServiceAccount
name: 'envoy-gateway'
namespace: '{{ .Release.Namespace }}'
{{ else }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: {{ include "eg.fullname" . }}-envoy-gateway-role
rules:
{{ include "eg.rbac.cluster" . }}
{{ include "eg.rbac.namespaced" . }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ include "eg.fullname" . }}-envoy-gateway-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ include "eg.fullname" . }}-envoy-gateway-role
subjects:
- kind: ServiceAccount
name: 'envoy-gateway'
namespace: '{{ .Release.Namespace }}'
{{ end }}

View file

@ -0,0 +1,30 @@
apiVersion: v1
kind: Service
metadata:
name: envoy-gateway
namespace: '{{ .Release.Namespace }}'
{{- with .Values.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
labels:
control-plane: envoy-gateway
{{- include "eg.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
{{- if and (eq .Values.service.type "LoadBalancer") .Values.service.loadBalancerIP }}
loadBalancerIP: {{ .Values.service.loadBalancerIP }}
{{- end }}
selector:
control-plane: envoy-gateway
{{- include "eg.selectorLabels" . | nindent 4 }}
ports:
{{- .Values.deployment.ports | toYaml | nindent 2 -}}
{{- if .Values.topologyInjector.enabled }}
- name: webhook
port: 9443
targetPort: 9443
{{- end }}
{{- with .Values.service.trafficDistribution }}
trafficDistribution: {{ . }}
{{- end }}

View file

@ -0,0 +1,7 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: envoy-gateway
namespace: '{{ .Release.Namespace }}'
labels:
{{- include "eg.labels" . | nindent 4 }}

View file

@ -0,0 +1,63 @@
{{- if .Values.topologyInjector.enabled }}
{{ $watchedNamespaces := list }}
{{ $gatewayNamespaceMode := false}}
{{- if .Values.config.envoyGateway.provider.kubernetes }}
{{- $kube := .Values.config.envoyGateway.provider.kubernetes }}
{{- $gatewayNamespaceMode = and ($kube.deploy) (eq $kube.deploy.type "GatewayNamespace") }}
{{- if $kube.watch }}
{{- if $kube.watch.namespaces }}
{{- if gt (len $kube.watch.namespaces) 0 }}
{{- $watchedNamespaces = $kube.watch.namespaces }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: 'envoy-gateway-topology-injector.{{ .Release.Namespace }}'
annotations:
"helm.sh/hook": pre-install, pre-upgrade
"helm.sh/hook-weight": "-1"
{{- if .Values.topologyInjector.annotations }}
{{- toYaml .Values.topologyInjector.annotations | nindent 4 -}}
{{- end }}
labels:
app.kubernetes.io/component: topology-injector
{{- include "eg.labels" . | nindent 4 }}
webhooks:
- name: topology.webhook.gateway.envoyproxy.io
admissionReviewVersions: ["v1"]
sideEffects: None
clientConfig:
service:
name: envoy-gateway
namespace: '{{ .Release.Namespace }}'
path: "/inject-pod-topology"
port: 9443
failurePolicy: Ignore
rules:
- operations: ["CREATE"]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods/binding"]
{{- if not $gatewayNamespaceMode }}
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: In
values:
- {{ .Release.Namespace }}
{{- else if gt (len $watchedNamespaces) 0 }}
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: In
values:
{{- range $watchedNamespaces }}
- {{ . | quote }}
{{- end }}
{{- end }}
{{- end }}

View file

@ -0,0 +1,60 @@
{{ if .Values.config.envoyGateway.provider.kubernetes }}
{{ $kube := .Values.config.envoyGateway.provider.kubernetes }}
{{/* Create ClusterRole for GatewayNamespace mode when:
1. No watch config is set, OR
2. Watch is configured with type NamespaceSelector
*/}}
{{ if and ($kube.deploy) (eq $kube.deploy.type "GatewayNamespace") (or (not $kube.watch) (and $kube.watch (eq $kube.watch.type "NamespaceSelector"))) }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ include "eg.fullname" $ }}-cluster-infra-manager
labels:
{{- include "eg.labels" $ | nindent 4 }}
rules:
{{ include "eg.rbac.infra.basic" . }}
{{ include "eg.rbac.infra.tokenreview" . }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ include "eg.fullname" $ }}-cluster-infra-manager
labels:
{{- include "eg.labels" $ | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: '{{ include "eg.fullname" $ }}-cluster-infra-manager'
subjects:
- kind: ServiceAccount
name: 'envoy-gateway'
namespace: '{{ $.Release.Namespace }}'
---
{{ end }}
{{ end }}
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ include "eg.fullname" . }}-infra-manager
namespace: '{{ .Release.Namespace }}'
labels:
{{- include "eg.labels" . | nindent 4 }}
rules:
{{ include "eg.rbac.infra.basic" . }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ include "eg.fullname" . }}-infra-manager
namespace: '{{ .Release.Namespace }}'
labels:
{{- include "eg.labels" . | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: '{{ include "eg.fullname" . }}-infra-manager'
subjects:
- kind: ServiceAccount
name: 'envoy-gateway'
namespace: '{{ .Release.Namespace }}'

View file

@ -0,0 +1,55 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ include "eg.fullname" . }}-leader-election-role
namespace: '{{ .Release.Namespace }}'
labels:
{{- include "eg.labels" . | nindent 4 }}
rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- coordination.k8s.io
resources:
- leases
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ include "eg.fullname" . }}-leader-election-rolebinding
namespace: '{{ .Release.Namespace }}'
labels:
{{- include "eg.labels" . | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: '{{ include "eg.fullname" . }}-leader-election-role'
subjects:
- kind: ServiceAccount
name: 'envoy-gateway'
namespace: '{{ .Release.Namespace }}'

View file

@ -0,0 +1,6 @@
{{ if .Values.createNamespace }}
apiVersion: v1
kind: Namespace
metadata:
name: '{{ .Release.Namespace }}'
{{ end }}

View file

@ -0,0 +1,66 @@
{{ $watchedNamespaces := list }}
{{ if .Values.config.envoyGateway.provider.kubernetes }}
{{ $kube := .Values.config.envoyGateway.provider.kubernetes }}
{{ if and ($kube.watch) ($kube.deploy) (eq $kube.deploy.type "GatewayNamespace") }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ include "eg.fullname" $ }}-infra-manager-tokenreview
labels:
{{- include "eg.labels" $ | nindent 4 }}
rules:
{{ include "eg.rbac.infra.tokenreview" . }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ include "eg.fullname" $ }}-infra-manager-tokenreview
labels:
{{- include "eg.labels" $ | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: '{{ include "eg.fullname" $ }}-infra-manager-tokenreview'
subjects:
- kind: ServiceAccount
name: 'envoy-gateway'
namespace: '{{ $.Release.Namespace }}'
{{ if $kube.watch.namespaces }}
{{ if gt (len $kube.watch.namespaces) 0 }}
{{ $watchedNamespaces = $kube.watch.namespaces }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{ if gt (len $watchedNamespaces) 0 }}
{{ range $_, $ns := $watchedNamespaces }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ include "eg.fullname" $ }}-namespaced-infra-manager
namespace: {{ $ns | quote }}
labels:
{{- include "eg.labels" $ | nindent 4 }}
rules:
{{ include "eg.rbac.infra.basic" . }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ include "eg.fullname" $ }}-namespaced-infra-manager
namespace: {{ $ns | quote }}
labels:
{{- include "eg.labels" $ | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: '{{ include "eg.fullname" $ }}-namespaced-infra-manager'
subjects:
- kind: ServiceAccount
name: 'envoy-gateway'
namespace: '{{ $.Release.Namespace }}'
---
{{- end }}
{{- end }}

View file

@ -0,0 +1,147 @@
# Global settings
global:
# If set, these take highest precedence and change both envoyGateway and ratelimit's container registry and pull secrets.
# -- Global override for image registry
imageRegistry: ""
# -- Global override for image pull secrets
imagePullSecrets: []
# If set, these override image-specific values: useful when installing the chart in a private registry environment.
# Override image-specific values directly if a global override is not desired.
images:
envoyGateway:
# This is the full image name including the hub, repo, and tag.
image: docker.io/envoyproxy/gateway:v1.7.1
# Specify image pull policy if default behavior isn't desired.
# Default behavior: latest images will be Always else IfNotPresent.
pullPolicy: IfNotPresent
# List of secrets in the same namespace of the component that can be used to pull images from private repositories.
pullSecrets: []
ratelimit:
# This is the full image name including the hub, repo, and tag.
image: "docker.io/envoyproxy/ratelimit:c8765e89"
# Specify image pull policy if default behavior isn't desired.
# Default behavior: latest images will be Always else IfNotPresent.
pullPolicy: IfNotPresent
# List of secrets in the same namespace of the component that can be used to pull images from private repositories.
pullSecrets: []
podDisruptionBudget:
minAvailable: 0
# maxUnavailable: 1
deployment:
annotations: {}
envoyGateway:
image:
# if both this and global.imageRegistry are specified, this has to include both registry and repository explicitly, eg docker.io/envoyproxy/gateway
repository: ""
tag: ""
imagePullPolicy: ""
imagePullSecrets: []
resources:
limits:
memory: 1024Mi
requests:
cpu: 100m
memory: 256Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
runAsNonRoot: true
runAsGroup: 65532
runAsUser: 65532
seccompProfile:
type: RuntimeDefault
ports:
- name: grpc
port: 18000
targetPort: 18000
- name: ratelimit
port: 18001
targetPort: 18001
- name: wasm
port: 18002
targetPort: 18002
- name: metrics
port: 19001
targetPort: 19001
priorityClassName: null
replicas: 1
pod:
affinity: {}
annotations:
prometheus.io/scrape: 'true'
prometheus.io/port: '19001'
labels: {}
topologySpreadConstraints: []
tolerations: []
nodeSelector: {}
service:
# If set to PreferClose, the Envoy fleet will prioritize connecting to the Envoy Gateway pods that are topologically closest to them.
trafficDistribution: ""
annotations: {}
# -- Service type. Can be set to LoadBalancer with specific IP, e.g.:
# type: LoadBalancer
# loadBalancerIP: 10.236.90.20
type: "ClusterIP"
hpa:
enabled: false
minReplicas: 1
maxReplicas: 1
metrics: []
behavior: {}
config:
# -- EnvoyGateway configuration. Visit https://gateway.envoyproxy.io/docs/api/extension_types/#envoygateway to view all options.
envoyGateway:
gateway:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
provider:
type: Kubernetes
logging:
level:
default: info
extensionApis: {}
createNamespace: false
kubernetesClusterDomain: cluster.local
# -- Certgen is used to generate the certificates required by EnvoyGateway. If you want to construct a custom certificate, you can generate a custom certificate through Cert-Manager before installing EnvoyGateway. Certgen will not overwrite the custom certificate. Please do not manually modify `values.yaml` to disable certgen, it may cause EnvoyGateway OIDC,OAuth2,etc. to not work as expected.
certgen:
job:
annotations: {}
args: []
pod:
annotations: {}
labels: {}
resources: {}
affinity: {}
tolerations: []
nodeSelector: {}
ttlSecondsAfterFinished: 30
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsGroup: 65532
runAsUser: 65532
seccompProfile:
type: RuntimeDefault
rbac:
annotations: {}
labels: {}
topologyInjector:
enabled: true
annotations: {}

View file

@ -0,0 +1,476 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.18.0
name: backends.gateway.envoyproxy.io
spec:
group: gateway.envoyproxy.io
names:
categories:
- envoy-gateway
kind: Backend
listKind: BackendList
plural: backends
shortNames:
- be
singular: backend
scope: Namespaced
versions:
- additionalPrinterColumns:
- jsonPath: .status.conditions[?(@.type=="Accepted")].reason
name: Status
type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1alpha1
schema:
openAPIV3Schema:
description: |-
Backend allows the user to configure the endpoints of a backend and
the behavior of the connection from Envoy Proxy to the backend.
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: Spec defines the desired state of Backend.
properties:
appProtocols:
description: AppProtocols defines the application protocols to be
supported when connecting to the backend.
items:
description: AppProtocolType defines various backend applications
protocols supported by Envoy Gateway
enum:
- gateway.envoyproxy.io/h2c
- gateway.envoyproxy.io/ws
- gateway.envoyproxy.io/wss
type: string
type: array
endpoints:
description: Endpoints defines the endpoints to be used when connecting
to the backend.
items:
description: |-
BackendEndpoint describes a backend endpoint, which can be either a fully-qualified domain name, IP address or unix domain socket
corresponding to Envoy's Address: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/address.proto#config-core-v3-address
properties:
fqdn:
description: FQDN defines a FQDN endpoint
properties:
hostname:
description: Hostname defines the FQDN hostname of the backend
endpoint.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
port:
description: Port defines the port of the backend endpoint.
format: int32
maximum: 65535
minimum: 0
type: integer
required:
- hostname
- port
type: object
hostname:
description: Hostname defines an optional hostname for the backend
endpoint.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
ip:
description: IP defines an IP endpoint. Supports both IPv4 and
IPv6 addresses.
properties:
address:
description: |-
Address defines the IP address of the backend endpoint.
Supports both IPv4 and IPv6 addresses.
maxLength: 45
minLength: 3
pattern: ^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|^(([0-9a-fA-F]{1,4}:){1,7}[0-9a-fA-F]{1,4}|::|(([0-9a-fA-F]{1,4}:){0,5})?(:[0-9a-fA-F]{1,4}){1,2})$
type: string
port:
description: Port defines the port of the backend endpoint.
format: int32
maximum: 65535
minimum: 0
type: integer
required:
- address
- port
type: object
unix:
description: Unix defines the unix domain socket endpoint
properties:
path:
description: |-
Path defines the unix domain socket path of the backend endpoint.
The path length must not exceed 108 characters.
type: string
x-kubernetes-validations:
- message: unix domain socket path must not exceed 108 characters
rule: size(self) <= 108
required:
- path
type: object
zone:
description: Zone defines the service zone of the backend endpoint.
type: string
type: object
x-kubernetes-validations:
- message: one of fqdn, ip or unix must be specified
rule: (has(self.fqdn) || has(self.ip) || has(self.unix))
- message: only one of fqdn, ip or unix can be specified
rule: ((has(self.fqdn) && !(has(self.ip) || has(self.unix))) ||
(has(self.ip) && !(has(self.fqdn) || has(self.unix))) || (has(self.unix)
&& !(has(self.ip) || has(self.fqdn))))
maxItems: 256
minItems: 1
type: array
x-kubernetes-validations:
- message: fqdn addresses cannot be mixed with other address types
rule: self.all(f, has(f.fqdn)) || !self.exists(f, has(f.fqdn))
fallback:
description: |-
Fallback indicates whether the backend is designated as a fallback.
It is highly recommended to configure active or passive health checks to ensure that failover can be detected
when the active backends become unhealthy and to automatically readjust once the primary backends are healthy again.
The overprovisioning factor is set to 1.4, meaning the fallback backends will only start receiving traffic when
the health of the active backends falls below 72%.
type: boolean
tls:
description: |-
TLS defines the TLS settings for the backend.
If TLS is specified here and a BackendTLSPolicy is also configured for the backend, the final TLS settings will
be a merge of both configurations. In case of overlapping fields, the values defined in the BackendTLSPolicy will
take precedence.
properties:
alpnProtocols:
description: |-
ALPNProtocols supplies the list of ALPN protocols that should be
exposed by the listener or used by the proxy to connect to the backend.
Defaults:
1. HTTPS Routes: h2 and http/1.1 are enabled in listener context.
2. Other Routes: ALPN is disabled.
3. Backends: proxy uses the appropriate ALPN options for the backend protocol.
When an empty list is provided, the ALPN TLS extension is disabled.
Defaults to [h2, http/1.1] if not specified.
Typical Supported values are:
- http/1.0
- http/1.1
- h2
items:
description: ALPNProtocol specifies the protocol to be negotiated
using ALPN
type: string
type: array
caCertificateRefs:
description: |-
CACertificateRefs contains one or more references to Kubernetes objects that
contain TLS certificates of the Certificate Authorities that can be used
as a trust anchor to validate the certificates presented by the backend.
A single reference to a Kubernetes ConfigMap or a Kubernetes Secret,
with the CA certificate in a key named `ca.crt` is currently supported.
If CACertificateRefs is empty or unspecified, then WellKnownCACertificates must be
specified. Only one of CACertificateRefs or WellKnownCACertificates may be specified,
not both.
items:
description: |-
LocalObjectReference identifies an API object within the namespace of the
referrer.
The API object must be valid in the cluster; the Group and Kind must
be registered in the cluster for this reference to be valid.
References to objects with invalid Group and Kind are not valid, and must
be rejected by the implementation, with appropriate Conditions set
on the containing object.
properties:
group:
description: |-
Group is the group of the referent. For example, "gateway.networking.k8s.io".
When unspecified or empty string, core API group is inferred.
maxLength: 253
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
kind:
description: Kind is kind of the referent. For example "HTTPRoute"
or "Service".
maxLength: 63
minLength: 1
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
type: string
name:
description: Name is the name of the referent.
maxLength: 253
minLength: 1
type: string
required:
- group
- kind
- name
type: object
maxItems: 8
type: array
ciphers:
description: |-
Ciphers specifies the set of cipher suites supported when
negotiating TLS 1.0 - 1.2. This setting has no effect for TLS 1.3.
In non-FIPS Envoy Proxy builds the default cipher list is:
- [ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305]
- [ECDHE-RSA-AES128-GCM-SHA256|ECDHE-RSA-CHACHA20-POLY1305]
- ECDHE-ECDSA-AES256-GCM-SHA384
- ECDHE-RSA-AES256-GCM-SHA384
In builds using BoringSSL FIPS the default cipher list is:
- ECDHE-ECDSA-AES128-GCM-SHA256
- ECDHE-RSA-AES128-GCM-SHA256
- ECDHE-ECDSA-AES256-GCM-SHA384
- ECDHE-RSA-AES256-GCM-SHA384
items:
type: string
type: array
clientCertificateRef:
description: |-
ClientCertificateRef defines the reference to a Kubernetes Secret that contains
the client certificate and private key for Envoy to use when connecting to
backend services and external services, such as ExtAuth, ALS, OpenTelemetry, etc.
This secret should be located within the same namespace as the Envoy proxy resource that references it.
properties:
group:
default: ""
description: |-
Group is the group of the referent. For example, "gateway.networking.k8s.io".
When unspecified or empty string, core API group is inferred.
maxLength: 253
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
kind:
default: Secret
description: Kind is kind of the referent. For example "Secret".
maxLength: 63
minLength: 1
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
type: string
name:
description: Name is the name of the referent.
maxLength: 253
minLength: 1
type: string
namespace:
description: |-
Namespace is the namespace of the referenced object. When unspecified, the local
namespace is inferred.
Note that when a namespace different than the local namespace is specified,
a ReferenceGrant object is required in the referent namespace to allow that
namespace's owner to accept the reference. See the ReferenceGrant
documentation for details.
Support: Core
maxLength: 63
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
type: object
ecdhCurves:
description: |-
ECDHCurves specifies the set of supported ECDH curves.
In non-FIPS Envoy Proxy builds the default curves are:
- X25519
- P-256
In builds using BoringSSL FIPS the default curve is:
- P-256
items:
type: string
type: array
insecureSkipVerify:
default: false
description: |-
InsecureSkipVerify indicates whether the upstream's certificate verification
should be skipped. Defaults to "false".
type: boolean
maxVersion:
description: |-
Max specifies the maximal TLS protocol version to allow
The default is TLS 1.3 if this is not specified.
enum:
- Auto
- "1.0"
- "1.1"
- "1.2"
- "1.3"
type: string
minVersion:
description: |-
Min specifies the minimal TLS protocol version to allow.
The default is TLS 1.2 if this is not specified.
enum:
- Auto
- "1.0"
- "1.1"
- "1.2"
- "1.3"
type: string
signatureAlgorithms:
description: |-
SignatureAlgorithms specifies which signature algorithms the listener should
support.
items:
type: string
type: array
sni:
description: |-
SNI is specifies the SNI value used when establishing an upstream TLS connection to the backend.
Envoy Gateway will use the HTTP host header value for SNI, when all resources referenced in BackendRefs are:
1. Backend resources that do not set SNI, or
2. Service/ServiceImport resources that do not have a BackendTLSPolicy attached to them
When a BackendTLSPolicy attaches to a Backend resource, the BackendTLSPolicy's Hostname value takes precedence
over this value.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
wellKnownCACertificates:
description: |-
WellKnownCACertificates specifies whether system CA certificates may be used in
the TLS handshake between the gateway and backend pod.
If WellKnownCACertificates is unspecified or empty (""), then CACertificateRefs
must be specified with at least one entry for a valid configuration. Only one of
CACertificateRefs or WellKnownCACertificates may be specified, not both.
enum:
- System
type: string
type: object
x-kubernetes-validations:
- message: must not contain both CACertificateRefs and WellKnownCACertificates
rule: '!(has(self.caCertificateRefs) && size(self.caCertificateRefs)
> 0 && has(self.wellKnownCACertificates) && self.wellKnownCACertificates
!= "")'
- message: must not contain either CACertificateRefs or WellKnownCACertificates
when InsecureSkipVerify is enabled
rule: '!((has(self.insecureSkipVerify) && self.insecureSkipVerify)
&& ((has(self.caCertificateRefs) && size(self.caCertificateRefs)
> 0) || (has(self.wellKnownCACertificates) && self.wellKnownCACertificates
!= "")))'
- message: setting ciphers has no effect if the minimum possible TLS
version is 1.3
rule: 'has(self.minVersion) && self.minVersion == ''1.3'' ? !has(self.ciphers)
: true'
- message: minVersion must be smaller or equal to maxVersion
rule: 'has(self.minVersion) && has(self.maxVersion) ? {"Auto":0,"1.0":1,"1.1":2,"1.2":3,"1.3":4}[self.minVersion]
<= {"1.0":1,"1.1":2,"1.2":3,"1.3":4,"Auto":5}[self.maxVersion]
: !has(self.minVersion) && has(self.maxVersion) ? 3 <= {"1.0":1,"1.1":2,"1.2":3,"1.3":4,"Auto":5}[self.maxVersion]
: true'
type:
default: Endpoints
description: Type defines the type of the backend. Defaults to "Endpoints"
enum:
- Endpoints
- DynamicResolver
type: string
type: object
x-kubernetes-validations:
- message: DynamicResolver type cannot have endpoints specified
rule: self.type != 'DynamicResolver' || !has(self.endpoints)
status:
description: Status defines the current status of Backend.
properties:
conditions:
description: Conditions describe the current conditions of the Backend.
items:
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
maxItems: 8
type: array
x-kubernetes-list-map-keys:
- type
x-kubernetes-list-type: map
type: object
required:
- spec
type: object
served: true
storage: true
subresources:
status: {}

View file

@ -0,0 +1,510 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.18.0
name: envoypatchpolicies.gateway.envoyproxy.io
spec:
group: gateway.envoyproxy.io
names:
categories:
- envoy-gateway
kind: EnvoyPatchPolicy
listKind: EnvoyPatchPolicyList
plural: envoypatchpolicies
shortNames:
- epp
singular: envoypatchpolicy
scope: Namespaced
versions:
- additionalPrinterColumns:
- jsonPath: .status.ancestors[0].conditions[?(@.type=="Accepted")].status
name: Accepted
type: string
- jsonPath: .status.ancestors[0].conditions[?(@.type=="Programmed")].status
name: Programmed
type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1alpha1
schema:
openAPIV3Schema:
description: |-
EnvoyPatchPolicy allows the user to modify the generated Envoy xDS
resources by Envoy Gateway using this patch API
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: Spec defines the desired state of EnvoyPatchPolicy.
properties:
jsonPatches:
description: JSONPatch defines the JSONPatch configuration.
items:
description: |-
EnvoyJSONPatchConfig defines the configuration for patching a Envoy xDS Resource
using JSONPatch semantic
properties:
name:
description: Name is the name of the resource
type: string
operation:
description: Patch defines the JSON Patch Operation
properties:
from:
description: |-
From is the source location of the value to be copied or moved. Only valid
for move or copy operations
Refer to https://datatracker.ietf.org/doc/html/rfc6901 for more details.
type: string
jsonPath:
description: |-
JSONPath is a JSONPath expression. Refer to https://datatracker.ietf.org/doc/rfc9535/ for more details.
It produces one or more JSONPointer expressions based on the given JSON document.
If no JSONPointer is found, it will result in an error.
If the 'Path' property is also set, it will be appended to the resulting JSONPointer expressions from the JSONPath evaluation.
This is useful when creating a property that does not yet exist in the JSON document.
The final JSONPointer expressions specifies the locations in the target document/field where the operation will be applied.
type: string
op:
description: Op is the type of operation to perform
enum:
- add
- remove
- replace
- move
- copy
- test
type: string
path:
description: |-
Path is a JSONPointer expression. Refer to https://datatracker.ietf.org/doc/html/rfc6901 for more details.
It specifies the location of the target document/field where the operation will be performed
type: string
value:
description: |-
Value is the new value of the path location. The value is only used by
the `add` and `replace` operations.
x-kubernetes-preserve-unknown-fields: true
required:
- op
type: object
type:
description: Type is the typed URL of the Envoy xDS Resource
enum:
- type.googleapis.com/envoy.config.listener.v3.Listener
- type.googleapis.com/envoy.config.route.v3.RouteConfiguration
- type.googleapis.com/envoy.config.cluster.v3.Cluster
- type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment
- type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret
type: string
required:
- name
- operation
- type
type: object
type: array
priority:
description: |-
Priority of the EnvoyPatchPolicy.
If multiple EnvoyPatchPolicies are applied to the same
TargetRef, they will be applied in the ascending order of
the priority i.e. int32.min has the highest priority and
int32.max has the lowest priority.
Defaults to 0.
format: int32
type: integer
targetRef:
description: |-
TargetRef is the name of the Gateway API resource this policy
is being attached to.
By default, attaching to Gateway is supported and
when mergeGateways is enabled it should attach to GatewayClass.
This Policy and the TargetRef MUST be in the same namespace
for this Policy to have effect and be applied to the Gateway
TargetRef
properties:
group:
description: Group is the group of the target resource.
maxLength: 253
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
kind:
description: Kind is kind of the target resource.
maxLength: 63
minLength: 1
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
type: string
name:
description: Name is the name of the target resource.
maxLength: 253
minLength: 1
type: string
required:
- group
- kind
- name
type: object
type:
description: |-
Type decides the type of patch.
Valid EnvoyPatchType values are "JSONPatch".
enum:
- JSONPatch
type: string
required:
- targetRef
- type
type: object
status:
description: Status defines the current status of EnvoyPatchPolicy.
properties:
ancestors:
description: |-
Ancestors is a list of ancestor resources (usually Gateways) that are
associated with the policy, and the status of the policy with respect to
each ancestor. When this policy attaches to a parent, the controller that
manages the parent and the ancestors MUST add an entry to this list when
the controller first sees the policy and SHOULD update the entry as
appropriate when the relevant ancestor is modified.
Note that choosing the relevant ancestor is left to the Policy designers;
an important part of Policy design is designing the right object level at
which to namespace this status.
Note also that implementations MUST ONLY populate ancestor status for
the Ancestor resources they are responsible for. Implementations MUST
use the ControllerName field to uniquely identify the entries in this list
that they are responsible for.
Note that to achieve this, the list of PolicyAncestorStatus structs
MUST be treated as a map with a composite key, made up of the AncestorRef
and ControllerName fields combined.
A maximum of 16 ancestors will be represented in this list. An empty list
means the Policy is not relevant for any ancestors.
If this slice is full, implementations MUST NOT add further entries.
Instead they MUST consider the policy unimplementable and signal that
on any related resources such as the ancestor that would be referenced
here. For example, if this list was full on BackendTLSPolicy, no
additional Gateways would be able to reference the Service targeted by
the BackendTLSPolicy.
items:
description: |-
PolicyAncestorStatus describes the status of a route with respect to an
associated Ancestor.
Ancestors refer to objects that are either the Target of a policy or above it
in terms of object hierarchy. For example, if a policy targets a Service, the
Policy's Ancestors are, in order, the Service, the HTTPRoute, the Gateway, and
the GatewayClass. Almost always, in this hierarchy, the Gateway will be the most
useful object to place Policy status on, so we recommend that implementations
SHOULD use Gateway as the PolicyAncestorStatus object unless the designers
have a _very_ good reason otherwise.
In the context of policy attachment, the Ancestor is used to distinguish which
resource results in a distinct application of this policy. For example, if a policy
targets a Service, it may have a distinct result per attached Gateway.
Policies targeting the same resource may have different effects depending on the
ancestors of those resources. For example, different Gateways targeting the same
Service may have different capabilities, especially if they have different underlying
implementations.
For example, in BackendTLSPolicy, the Policy attaches to a Service that is
used as a backend in a HTTPRoute that is itself attached to a Gateway.
In this case, the relevant object for status is the Gateway, and that is the
ancestor object referred to in this status.
Note that a parent is also an ancestor, so for objects where the parent is the
relevant object for status, this struct SHOULD still be used.
This struct is intended to be used in a slice that's effectively a map,
with a composite key made up of the AncestorRef and the ControllerName.
properties:
ancestorRef:
description: |-
AncestorRef corresponds with a ParentRef in the spec that this
PolicyAncestorStatus struct describes the status of.
properties:
group:
default: gateway.networking.k8s.io
description: |-
Group is the group of the referent.
When unspecified, "gateway.networking.k8s.io" is inferred.
To set the core API group (such as for a "Service" kind referent),
Group must be explicitly set to "" (empty string).
Support: Core
maxLength: 253
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
kind:
default: Gateway
description: |-
Kind is kind of the referent.
There are two kinds of parent resources with "Core" support:
* Gateway (Gateway conformance profile)
* Service (Mesh conformance profile, ClusterIP Services only)
Support for other resources is Implementation-Specific.
maxLength: 63
minLength: 1
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
type: string
name:
description: |-
Name is the name of the referent.
Support: Core
maxLength: 253
minLength: 1
type: string
namespace:
description: |-
Namespace is the namespace of the referent. When unspecified, this refers
to the local namespace of the Route.
Note that there are specific rules for ParentRefs which cross namespace
boundaries. Cross-namespace references are only valid if they are explicitly
allowed by something in the namespace they are referring to. For example:
Gateway has the AllowedRoutes field, and ReferenceGrant provides a
generic way to enable any other kind of cross-namespace reference.
<gateway:experimental:description>
ParentRefs from a Route to a Service in the same namespace are "producer"
routes, which apply default routing rules to inbound connections from
any namespace to the Service.
ParentRefs from a Route to a Service in a different namespace are
"consumer" routes, and these routing rules are only applied to outbound
connections originating from the same namespace as the Route, for which
the intended destination of the connections are a Service targeted as a
ParentRef of the Route.
</gateway:experimental:description>
Support: Core
maxLength: 63
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
port:
description: |-
Port is the network port this Route targets. It can be interpreted
differently based on the type of parent resource.
When the parent resource is a Gateway, this targets all listeners
listening on the specified port that also support this kind of Route(and
select this Route). It's not recommended to set `Port` unless the
networking behaviors specified in a Route must apply to a specific port
as opposed to a listener(s) whose port(s) may be changed. When both Port
and SectionName are specified, the name and port of the selected listener
must match both specified values.
<gateway:experimental:description>
When the parent resource is a Service, this targets a specific port in the
Service spec. When both Port (experimental) and SectionName are specified,
the name and port of the selected port must match both specified values.
</gateway:experimental:description>
Implementations MAY choose to support other parent resources.
Implementations supporting other types of parent resources MUST clearly
document how/if Port is interpreted.
For the purpose of status, an attachment is considered successful as
long as the parent resource accepts it partially. For example, Gateway
listeners can restrict which Routes can attach to them by Route kind,
namespace, or hostname. If 1 of 2 Gateway listeners accept attachment
from the referencing Route, the Route MUST be considered successfully
attached. If no Gateway listeners accept attachment from this Route,
the Route MUST be considered detached from the Gateway.
Support: Extended
format: int32
maximum: 65535
minimum: 1
type: integer
sectionName:
description: |-
SectionName is the name of a section within the target resource. In the
following resources, SectionName is interpreted as the following:
* Gateway: Listener name. When both Port (experimental) and SectionName
are specified, the name and port of the selected listener must match
both specified values.
* Service: Port name. When both Port (experimental) and SectionName
are specified, the name and port of the selected listener must match
both specified values.
Implementations MAY choose to support attaching Routes to other resources.
If that is the case, they MUST clearly document how SectionName is
interpreted.
When unspecified (empty string), this will reference the entire resource.
For the purpose of status, an attachment is considered successful if at
least one section in the parent resource accepts it. For example, Gateway
listeners can restrict which Routes can attach to them by Route kind,
namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from
the referencing Route, the Route MUST be considered successfully
attached. If no Gateway listeners accept attachment from this Route, the
Route MUST be considered detached from the Gateway.
Support: Core
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- name
type: object
conditions:
description: |-
Conditions describes the status of the Policy with respect to the given Ancestor.
<gateway:util:excludeFromCRD>
Notes for implementors:
Conditions are a listType `map`, which means that they function like a
map with a key of the `type` field _in the k8s apiserver_.
This means that implementations must obey some rules when updating this
section.
* Implementations MUST perform a read-modify-write cycle on this field
before modifying it. That is, when modifying this field, implementations
must be confident they have fetched the most recent version of this field,
and ensure that changes they make are on that recent version.
* Implementations MUST NOT remove or reorder Conditions that they are not
directly responsible for. For example, if an implementation sees a Condition
with type `special.io/SomeField`, it MUST NOT remove, change or update that
Condition.
* Implementations MUST always _merge_ changes into Conditions of the same Type,
rather than creating more than one Condition of the same Type.
* Implementations MUST always update the `observedGeneration` field of the
Condition to the `metadata.generation` of the Gateway at the time of update creation.
* If the `observedGeneration` of a Condition is _greater than_ the value the
implementation knows about, then it MUST NOT perform the update on that Condition,
but must wait for a future reconciliation and status update. (The assumption is that
the implementation's copy of the object is stale and an update will be re-triggered
if relevant.)
</gateway:util:excludeFromCRD>
items:
description: Condition contains details for one aspect of
the current state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False,
Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
maxItems: 8
minItems: 1
type: array
x-kubernetes-list-map-keys:
- type
x-kubernetes-list-type: map
controllerName:
description: |-
ControllerName is a domain/path string that indicates the name of the
controller that wrote this status. This corresponds with the
controllerName field on GatewayClass.
Example: "example.net/gateway-controller".
The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are
valid Kubernetes names
(https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names).
Controllers MUST populate this field when writing status. Controllers should ensure that
entries to status populated with their ControllerName are cleaned up when they are no
longer necessary.
maxLength: 253
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$
type: string
required:
- ancestorRef
- conditions
- controllerName
type: object
maxItems: 16
type: array
x-kubernetes-list-type: atomic
required:
- ancestors
type: object
required:
- spec
type: object
served: true
storage: true
subresources:
status: {}

View file

@ -0,0 +1,474 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.18.0
name: httproutefilters.gateway.envoyproxy.io
spec:
group: gateway.envoyproxy.io
names:
categories:
- envoy-gateway
kind: HTTPRouteFilter
listKind: HTTPRouteFilterList
plural: httproutefilters
shortNames:
- hrf
singular: httproutefilter
scope: Namespaced
versions:
- additionalPrinterColumns:
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1alpha1
schema:
openAPIV3Schema:
description: |-
HTTPRouteFilter is a custom Envoy Gateway HTTPRouteFilter which provides extended
traffic processing options such as path regex rewrite, direct response and more.
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: Spec defines the desired state of HTTPRouteFilter.
properties:
credentialInjection:
description: |-
HTTPCredentialInjectionFilter defines the configuration to inject credentials into the request.
This is useful when the backend service requires credentials in the request, and the original
request does not contain them. The filter can inject credentials into the request before forwarding
it to the backend service.
properties:
credential:
description: Credential is the credential to be injected.
properties:
valueRef:
description: |-
ValueRef is a reference to the secret containing the credentials to be injected.
This is an Opaque secret. The credential should be stored in the key
"credential", and the value should be the credential to be injected.
For example, for basic authentication, the value should be "Basic <base64 encoded username:password>".
for bearer token, the value should be "Bearer <token>".
Note: The secret must be in the same namespace as the HTTPRouteFilter.
properties:
group:
default: ""
description: |-
Group is the group of the referent. For example, "gateway.networking.k8s.io".
When unspecified or empty string, core API group is inferred.
maxLength: 253
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
kind:
default: Secret
description: Kind is kind of the referent. For example
"Secret".
maxLength: 63
minLength: 1
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
type: string
name:
description: Name is the name of the referent.
maxLength: 253
minLength: 1
type: string
namespace:
description: |-
Namespace is the namespace of the referenced object. When unspecified, the local
namespace is inferred.
Note that when a namespace different than the local namespace is specified,
a ReferenceGrant object is required in the referent namespace to allow that
namespace's owner to accept the reference. See the ReferenceGrant
documentation for details.
Support: Core
maxLength: 63
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
type: object
required:
- valueRef
type: object
header:
description: |-
Header is the name of the header where the credentials are injected.
If not specified, the credentials are injected into the Authorization header.
type: string
overwrite:
description: |-
Whether to overwrite the value or not if the injected headers already exist.
If not specified, the default value is false.
type: boolean
required:
- credential
type: object
directResponse:
description: HTTPDirectResponseFilter defines the configuration to
return a fixed response.
properties:
body:
description: Body of the direct response.
properties:
inline:
description: Inline contains the value as an inline string.
type: string
type:
allOf:
- enum:
- Inline
- ValueRef
- enum:
- Inline
- ValueRef
default: Inline
description: |-
Type is the type of method to use to read the body value.
Valid values are Inline and ValueRef, default is Inline.
type: string
valueRef:
description: |-
ValueRef contains the contents of the body
specified as a local object reference.
Only a reference to ConfigMap is supported.
The value of key `response.body` in the ConfigMap will be used as the response body.
If the key is not found, the first value in the ConfigMap will be used.
properties:
group:
description: |-
Group is the group of the referent. For example, "gateway.networking.k8s.io".
When unspecified or empty string, core API group is inferred.
maxLength: 253
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
kind:
description: Kind is kind of the referent. For example
"HTTPRoute" or "Service".
maxLength: 63
minLength: 1
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
type: string
name:
description: Name is the name of the referent.
maxLength: 253
minLength: 1
type: string
required:
- group
- kind
- name
type: object
required:
- type
type: object
x-kubernetes-validations:
- message: inline must be set for type Inline
rule: '(!has(self.type) || self.type == ''Inline'')? has(self.inline)
: true'
- message: valueRef must be set for type ValueRef
rule: '(has(self.type) && self.type == ''ValueRef'')? has(self.valueRef)
: true'
- message: only ConfigMap is supported for ValueRef
rule: 'has(self.valueRef) ? self.valueRef.kind == ''ConfigMap''
: true'
contentType:
description: Content Type of the direct response. This will be
set in the Content-Type header.
type: string
header:
description: Header defines the headers of the direct response.
properties:
add:
description: |-
Add adds the given header(s) (name, value) to the request
before the action. It appends to any existing values associated
with the header name.
Input:
GET /foo HTTP/1.1
my-header: foo
Config:
add:
- name: "my-header"
value: "bar,baz"
Output:
GET /foo HTTP/1.1
my-header: foo,bar,baz
items:
description: HTTPHeader represents an HTTP Header name and
value as defined by RFC 7230.
properties:
name:
description: |-
Name is the name of the HTTP Header to be matched. Name matching MUST be
case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2).
If multiple entries specify equivalent header names, the first entry with
an equivalent name MUST be considered for a match. Subsequent entries
with an equivalent header name MUST be ignored. Due to the
case-insensitivity of header names, "foo" and "Foo" are considered
equivalent.
maxLength: 256
minLength: 1
pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$
type: string
value:
description: Value is the value of HTTP Header to be
matched.
maxLength: 4096
minLength: 1
type: string
required:
- name
- value
type: object
maxItems: 16
type: array
x-kubernetes-list-map-keys:
- name
x-kubernetes-list-type: map
remove:
description: |-
Remove the given header(s) from the HTTP request before the action. The
value of Remove is a list of HTTP header names. Note that the header
names are case-insensitive (see
https://datatracker.ietf.org/doc/html/rfc2616#section-4.2).
Input:
GET /foo HTTP/1.1
my-header1: foo
my-header2: bar
my-header3: baz
Config:
remove: ["my-header1", "my-header3"]
Output:
GET /foo HTTP/1.1
my-header2: bar
items:
type: string
maxItems: 16
type: array
x-kubernetes-list-type: set
set:
description: |-
Set overwrites the request with the given header (name, value)
before the action.
Input:
GET /foo HTTP/1.1
my-header: foo
Config:
set:
- name: "my-header"
value: "bar"
Output:
GET /foo HTTP/1.1
my-header: bar
items:
description: HTTPHeader represents an HTTP Header name and
value as defined by RFC 7230.
properties:
name:
description: |-
Name is the name of the HTTP Header to be matched. Name matching MUST be
case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2).
If multiple entries specify equivalent header names, the first entry with
an equivalent name MUST be considered for a match. Subsequent entries
with an equivalent header name MUST be ignored. Due to the
case-insensitivity of header names, "foo" and "Foo" are considered
equivalent.
maxLength: 256
minLength: 1
pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$
type: string
value:
description: Value is the value of HTTP Header to be
matched.
maxLength: 4096
minLength: 1
type: string
required:
- name
- value
type: object
maxItems: 16
type: array
x-kubernetes-list-map-keys:
- name
x-kubernetes-list-type: map
type: object
x-kubernetes-validations:
- message: header.remove is not supported for DirectResponse
rule: '!has(self.remove) || size(self.remove) == 0'
statusCode:
description: |-
Status Code of the HTTP response
If unset, defaults to 200.
type: integer
type: object
matches:
description: |-
Matches defines additional matching criteria for the HTTPRoute rule.
As with HTTPRouteRule.Matches, the rule is matched if any one match applies.
When both HTTPRouteRule.Matches and HTTPRouteFilter.Matches are set, the
effective matching is the logical AND of the two sets.
items:
description: |-
HTTPRouteMatchFilter defines additional matching criteria for the HTTPRoute rule.
At least one matcher must be specified.
minProperties: 1
properties:
cookies:
description: |-
Cookies is a list of cookie matchers evaluated against the HTTP request.
All specified matchers must match.
items:
description: HTTPCookieMatch defines how to match a single
cookie.
properties:
name:
description: Name is the cookie name to evaluate.
maxLength: 256
minLength: 1
type: string
type:
default: Exact
description: Type specifies how to match against the value
of the cookie.
enum:
- Exact
- RegularExpression
type: string
value:
description: Value is the cookie value to be matched.
maxLength: 4096
minLength: 1
type: string
required:
- name
- value
type: object
maxItems: 16
minItems: 1
type: array
type: object
maxItems: 8
type: array
urlRewrite:
description: HTTPURLRewriteFilter define rewrites of HTTP URL components
such as path and host
properties:
hostname:
description: |-
Hostname is the value to be used to replace the Host header value during
forwarding.
properties:
header:
description: Header is the name of the header whose value
would be used to rewrite the Host header
type: string
type:
description: HTTPPathModifierType defines the type of Hostname
rewrite.
enum:
- Header
- Backend
type: string
required:
- type
type: object
x-kubernetes-validations:
- message: header must be nil if the type is not Header
rule: '!(has(self.header) && self.type != ''Header'')'
- message: header must be specified for Header type
rule: '!(!has(self.header) && self.type == ''Header'')'
path:
description: Path defines a path rewrite.
properties:
replaceRegexMatch:
description: |-
ReplaceRegexMatch defines a path regex rewrite. The path portions matched by the regex pattern are replaced by the defined substitution.
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-regex-rewrite
Some examples:
(1) replaceRegexMatch:
pattern: ^/service/([^/]+)(/.*)$
substitution: \2/instance/\1
Would transform /service/foo/v1/api into /v1/api/instance/foo.
(2) replaceRegexMatch:
pattern: one
substitution: two
Would transform /xxx/one/yyy/one/zzz into /xxx/two/yyy/two/zzz.
(3) replaceRegexMatch:
pattern: ^(.*?)one(.*)$
substitution: \1two\2
Would transform /xxx/one/yyy/one/zzz into /xxx/two/yyy/one/zzz.
(3) replaceRegexMatch:
pattern: (?i)/xxx/
substitution: /yyy/
Would transform path /aaa/XxX/bbb into /aaa/yyy/bbb (case-insensitive).
properties:
pattern:
description: |-
Pattern matches a regular expression against the value of the HTTP Path.The regex string must
adhere to the syntax documented in https://github.com/google/re2/wiki/Syntax.
minLength: 1
type: string
substitution:
description: |-
Substitution is an expression that replaces the matched portion.The expression may include numbered
capture groups that adhere to syntax documented in https://github.com/google/re2/wiki/Syntax.
type: string
required:
- pattern
- substitution
type: object
type:
description: HTTPPathModifierType defines the type of path
redirect or rewrite.
enum:
- ReplaceRegexMatch
type: string
required:
- type
type: object
x-kubernetes-validations:
- message: If HTTPPathModifier type is ReplaceRegexMatch, replaceRegexMatch
field needs to be set.
rule: 'self.type == ''ReplaceRegexMatch'' ? has(self.replaceRegexMatch)
: !has(self.replaceRegexMatch)'
type: object
type: object
required:
- spec
type: object
served: true
storage: true
subresources: {}

View file

@ -0,0 +1,5 @@
gateway-helm:
config:
envoyGateway:
gateway:
controllerName: gateway.envoyproxy.io/gatewayclass-controller

View file

@ -6,4 +6,4 @@ include ../../../hack/package.mk
update:
rm -rf templates
mkdir templates
kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd/experimental?ref=v1.2.0" > templates/crds-experimental.yaml
kubectl kustomize "github.com/kubernetes-sigs/gateway-api/config/crd/experimental?ref=v1.5.0" > templates/crds-experimental.yaml

View file

@ -0,0 +1,3 @@
apiVersion: v2
name: gateway-rd
version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process

View file

@ -0,0 +1,4 @@
export NAME=gateway-rd
export NAMESPACE=cozy-system
include ../../../hack/package.mk

View file

@ -0,0 +1,35 @@
apiVersion: cozystack.io/v1alpha1
kind: ApplicationDefinition
metadata:
name: gateway
spec:
application:
kind: Gateway
plural: gateways
singular: gateway
openAPISchema: |-
{"title":"Chart Values","type":"object","properties":{}}
release:
prefix: ""
labels:
sharding.fluxcd.io/key: tenants
internal.cozystack.io/tenantmodule: "true"
chartRef:
kind: ExternalArtifact
name: cozystack-gateway-application-default-gateway
namespace: cozy-system
dashboard:
category: Administration
singular: Gateway
plural: Gateways
name: gateway
description: Envoy Gateway API controller
module: true
icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9IiM2QzNGQzAiLz4KPHBhdGggZD0iTTcyIDMwTDEwNCA3Mkw3MiAxMTRMNDAgNzJMNzIgMzBaIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjYiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGZpbGw9Im5vbmUiLz4KPHBhdGggZD0iTTcyIDUwTDkwIDcyTDcyIDk0TDU0IDcyTDcyIDUwWiIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBmaWxsPSJub25lIi8+Cjwvc3ZnPgo=
keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "_cluster"], ["spec", "_namespace"]]
secrets:
exclude: []
include: []
services:
exclude: []
include: []

View file

@ -0,0 +1,4 @@
{{- range $path, $_ := .Files.Glob "cozyrds/*" }}
---
{{ $.Files.Get $path }}
{{- end }}

View file

@ -0,0 +1 @@
{}

View file

@ -0,0 +1,74 @@
{{- $gatewayAPI := (index .Values._cluster "gateway-api") | default "false" }}
{{- $clusterIssuer := (index .Values._cluster "issuer-name") | default "letsencrypt-prod" }}
{{- $host := index .Values._cluster "root-host" }}
{{- $hostnames := .Values._cluster.hostnames | default dict }}
{{- $gateway := ((.Values._namespace).gateway) | default (index .Values._cluster "gateway") | default "" }}
{{- $gatewayClassName := $gateway }}
{{- $keycloakHost := .Values.ingress.host | default (index $hostnames "keycloak") | default (printf "keycloak.%s" $host) }}
{{- $oidcEnabled := (index .Values._cluster "oidc-enabled") | default "false" }}
{{- if and (eq $gatewayAPI "true") (eq $oidcEnabled "true") }}
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: keycloak
annotations:
cert-manager.io/cluster-issuer: {{ $clusterIssuer }}
spec:
gatewayClassName: {{ $gatewayClassName }}
{{- if $gateway }}
infrastructure:
labels:
cozystack.io/gateway: {{ $gateway }}
{{- end }}
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: {{ $keycloakHost | quote }}
tls:
mode: Terminate
certificateRefs:
- name: keycloak-gateway-tls
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: keycloak-redirect-to-https
spec:
parentRefs:
- name: acme-challenge
namespace: {{ $gateway }}
sectionName: http
hostnames:
- {{ $keycloakHost | quote }}
rules:
- filters:
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: keycloak
spec:
parentRefs:
- name: keycloak
hostnames:
- {{ $keycloakHost | quote }}
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: keycloak-http
port: 8080
{{- end }}

View file

@ -128,6 +128,10 @@ spec:
value: https://{{ $ingressHost }}
- name: JAVA_OPTS_APPEND
value: "-Djgroups.dns.query=keycloak-headless.cozy-keycloak.svc.{{ $clusterDomain }}"
{{- if eq ((index .Values._cluster "gateway-api") | default "false") "true" }}
- name: KC_SPI_STICKY_SESSION_ENCODER_INFINISPAN_SHOULD_ATTACH_ROUTE
value: "false"
{{- end }}
ports:
- name: http
containerPort: 8080

View file

@ -0,0 +1,48 @@
{{- $gatewayAPI := (index .Values._cluster "gateway-api") | default "false" }}
{{- $host := index .Values._cluster "root-host" }}
{{- $hostnames := .Values._cluster.hostnames | default dict }}
{{- $exposeServices := splitList "," ((index .Values._cluster "expose-services") | default "") }}
{{- $gateway := ((.Values._namespace).gateway) | default (index .Values._cluster "gateway") | default "" }}
{{- $gatewayClassName := $gateway }}
{{- $cdiHost := index $hostnames "cdi-uploadproxy" | default (printf "cdi-uploadproxy.%s" $host) }}
{{- if and (eq $gatewayAPI "true") (has "cdi-uploadproxy" $exposeServices) }}
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: cdi-uploadproxy
spec:
gatewayClassName: {{ $gatewayClassName }}
{{- if $gateway }}
infrastructure:
labels:
cozystack.io/gateway: {{ $gateway }}
{{- end }}
listeners:
- name: tls-passthrough
protocol: TLS
port: 443
hostname: {{ $cdiHost | quote }}
tls:
mode: Passthrough
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: TLSRoute
metadata:
name: cdi-uploadproxy
spec:
parentRefs:
- name: cdi-uploadproxy
sectionName: tls-passthrough
hostnames:
- {{ $cdiHost | quote }}
rules:
- backendRefs:
- name: cdi-uploadproxy
port: 443
{{- end }}

View file

@ -0,0 +1,48 @@
{{- $gatewayAPI := (index .Values._cluster "gateway-api") | default "false" }}
{{- $host := index .Values._cluster "root-host" }}
{{- $hostnames := .Values._cluster.hostnames | default dict }}
{{- $exposeServices := splitList "," ((index .Values._cluster "expose-services") | default "") }}
{{- $gateway := ((.Values._namespace).gateway) | default (index .Values._cluster "gateway") | default "" }}
{{- $gatewayClassName := $gateway }}
{{- $vmExportHost := index $hostnames "vm-exportproxy" | default (printf "vm-exportproxy.%s" $host) }}
{{- if and (eq $gatewayAPI "true") (has "vm-exportproxy" $exposeServices) }}
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: vm-exportproxy
spec:
gatewayClassName: {{ $gatewayClassName }}
{{- if $gateway }}
infrastructure:
labels:
cozystack.io/gateway: {{ $gateway }}
{{- end }}
listeners:
- name: tls-passthrough
protocol: TLS
port: 443
hostname: {{ $vmExportHost | quote }}
tls:
mode: Passthrough
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: TLSRoute
metadata:
name: vm-exportproxy
spec:
parentRefs:
- name: vm-exportproxy
sectionName: tls-passthrough
hostnames:
- {{ $vmExportHost | quote }}
rules:
- backendRefs:
- name: vm-exportproxy
port: 443
{{- end }}

View file

@ -0,0 +1,52 @@
{{- $gatewayAPI := (index .Values._cluster "gateway-api") | default "false" }}
{{- $clusterIssuer := (index .Values._cluster "issuer-name") | default "letsencrypt-prod" }}
{{- $gateway := .Values._namespace.gateway | default "" }}
{{- $host := .Values._namespace.host }}
{{- $alertaHost := printf "alerta.%s" (.Values.host | default $host) }}
{{- $gatewayClassName := .Values._namespace.gateway | default "" }}
{{- if and (eq $gatewayAPI "true") (ne $gateway "") }}
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: alerta
annotations:
cert-manager.io/cluster-issuer: {{ $clusterIssuer }}
spec:
gatewayClassName: {{ $gatewayClassName }}
infrastructure:
labels:
cozystack.io/gateway: {{ $gateway }}
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: {{ $alertaHost | quote }}
tls:
mode: Terminate
certificateRefs:
- name: alerta-gateway-tls
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: alerta
spec:
parentRefs:
- name: alerta
hostnames:
- {{ $alertaHost | quote }}
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: alerta
port: 80
{{- end }}

View file

@ -0,0 +1,52 @@
{{- $gatewayAPI := (index .Values._cluster "gateway-api") | default "false" }}
{{- $clusterIssuer := (index .Values._cluster "issuer-name") | default "letsencrypt-prod" }}
{{- $gateway := .Values._namespace.gateway | default "" }}
{{- $host := .Values._namespace.host }}
{{- $grafanaHost := printf "grafana.%s" (.Values.host | default $host) }}
{{- $gatewayClassName := .Values._namespace.gateway | default "" }}
{{- if and (eq $gatewayAPI "true") (ne $gateway "") }}
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: grafana
annotations:
cert-manager.io/cluster-issuer: {{ $clusterIssuer }}
spec:
gatewayClassName: {{ $gatewayClassName }}
infrastructure:
labels:
cozystack.io/gateway: {{ $gateway }}
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: {{ $grafanaHost | quote }}
tls:
mode: Terminate
certificateRefs:
- name: grafana-gateway-tls
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: grafana
spec:
parentRefs:
- name: grafana
hostnames:
- {{ $grafanaHost | quote }}
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: grafana-service
port: 3000
{{- end }}

View file

@ -8,7 +8,7 @@ spec:
singular: tenant
plural: tenants
openAPISchema: |-
{"title":"Chart Values","type":"object","properties":{"host":{"description":"The hostname used to access tenant services (defaults to using the tenant name as a subdomain for its parent tenant host).","type":"string","default":""},"etcd":{"description":"Deploy own Etcd cluster.","type":"boolean","default":false},"monitoring":{"description":"Deploy own Monitoring Stack.","type":"boolean","default":false},"ingress":{"description":"Deploy own Ingress Controller.","type":"boolean","default":false},"seaweedfs":{"description":"Deploy own SeaweedFS.","type":"boolean","default":false},"schedulingClass":{"description":"The name of a SchedulingClass CR to apply scheduling constraints for this tenant's workloads.","type":"string","default":""},"resourceQuotas":{"description":"Define resource quotas for the tenant.","type":"object","default":{},"additionalProperties":{"pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}}}
{"title":"Chart Values","type":"object","properties":{"host":{"description":"The hostname used to access tenant services (defaults to using the tenant name as a subdomain for its parent tenant host).","type":"string","default":""},"etcd":{"description":"Deploy own Etcd cluster.","type":"boolean","default":false},"monitoring":{"description":"Deploy own Monitoring Stack.","type":"boolean","default":false},"ingress":{"description":"Deploy own Ingress Controller.","type":"boolean","default":false},"gateway":{"description":"Deploy own Gateway API gateway (separate LB for this tenant).","type":"boolean","default":false},"seaweedfs":{"description":"Deploy own SeaweedFS.","type":"boolean","default":false},"schedulingClass":{"description":"The name of a SchedulingClass CR to apply scheduling constraints for this tenant's workloads.","type":"string","default":""},"resourceQuotas":{"description":"Define resource quotas for the tenant.","type":"object","default":{},"additionalProperties":{"pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}}}
release:
prefix: tenant-
labels:
@ -23,7 +23,7 @@ spec:
plural: Tenants
description: Separated tenant namespace
icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODdfMzQwMykiLz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzY4N18zNDAzKSI+CjxwYXRoIGQ9Ik03MiAyOUM2Ni4zOTI2IDI5IDYxLjAxNDggMzEuMjM4OCA1Ny4wNDk3IDM1LjIyNEM1My4wODQ3IDM5LjIwOTEgNTAuODU3MSA0NC42MTQxIDUwLjg1NzEgNTAuMjVDNTAuODU3MSA1NS44ODU5IDUzLjA4NDcgNjEuMjkwOSA1Ny4wNDk3IDY1LjI3NkM2MS4wMTQ4IDY5LjI2MTIgNjYuMzkyNiA3MS41IDcyIDcxLjVDNzcuNjA3NCA3MS41IDgyLjk4NTIgNjkuMjYxMiA4Ni45NTAzIDY1LjI3NkM5MC45MTUzIDYxLjI5MDkgOTMuMTQyOSA1NS44ODU5IDkzLjE0MjkgNTAuMjVDOTMuMTQyOSA0NC42MTQxIDkwLjkxNTMgMzkuMjA5MSA4Ni45NTAzIDM1LjIyNEM4Mi45ODUyIDMxLjIzODggNzcuNjA3NCAyOSA3MiAyOVpNNjAuOTgyNiA4My4zMDM3QzYwLjQ1NCA4Mi41ODk4IDU5LjU5NTEgODIuMTkxNCA1OC43MTk2IDgyLjI3NDRDNDUuMzg5NyA4My43MzU0IDM1IDk1LjEwNzQgMzUgMTA4LjkwM0MzNSAxMTEuNzI2IDM3LjI3OTUgMTE0IDQwLjA3MSAxMTRIMTAzLjkyOUMxMDYuNzM3IDExNCAxMDkgMTExLjcwOSAxMDkgMTA4LjkwM0MxMDkgOTUuMTA3NCA5OC42MTAzIDgzLjc1MiA4NS4yNjM4IDgyLjI5MUM4NC4zODg0IDgyLjE5MTQgODMuNTI5NSA4Mi42MDY0IDgzLjAwMDkgODMuMzIwM0w3NC4wOTc4IDk1LjI0MDJDNzMuMDQwNiA5Ni42NTE0IDcwLjkyNjMgOTYuNjUxNCA2OS44NjkyIDk1LjI0MDJMNjAuOTY2MSA4My4zMjAzTDYwLjk4MjYgODMuMzAzN1oiIGZpbGw9ImJsYWNrIi8+CjwvZz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl82ODdfMzQwMyIgeDE9IjcyIiB5MT0iMTQ0IiB4Mj0iLTEuMjgxN2UtMDUiIHkyPSI0IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiNDMEQ2RkYiLz4KPHN0b3Agb2Zmc2V0PSIwLjMiIHN0b3AtY29sb3I9IiNDNERBRkYiLz4KPHN0b3Agb2Zmc2V0PSIwLjY1IiBzdG9wLWNvbG9yPSIjRDNFOUZGIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI0U5RkZGRiIvPgo8L2xpbmVhckdyYWRpZW50Pgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzY4N18zNDAzIj4KPHJlY3Qgd2lkdGg9Ijc0IiBoZWlnaHQ9Ijg1IiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMzUgMjkpIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==
keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "host"], ["spec", "etcd"], ["spec", "monitoring"], ["spec", "ingress"], ["spec", "seaweedfs"], ["spec", "schedulingClass"], ["spec", "resourceQuotas"]]
keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "host"], ["spec", "etcd"], ["spec", "monitoring"], ["spec", "ingress"], ["spec", "gateway"], ["spec", "seaweedfs"], ["spec", "schedulingClass"], ["spec", "resourceQuotas"]]
secrets:
exclude: []
include: []