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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions charts/qtodo-db/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,42 @@ Create the image path for the passed in image field.
{{- printf "%s:%s" $name (tpl .value.version .context) -}}
{{- end -}}
{{- end -}}

{{/*
True when the shared secondary UDN is enabled (bool or string "true").
*/}}
{{- define "qtodo-db.udn.enabled" -}}
{{- if eq (.Values.udn.enabled | default false | toString) "true" -}}
true
{{- end -}}
{{- end -}}

{{/*
IPv4 prefix length from udn.subnet (e.g. 10.100.0.0/16 -> 16).
*/}}
{{- define "qtodo-db.udn.prefixlen" -}}
{{- regexReplaceAll "^[^/]+/" .Values.udn.subnet "" -}}
{{- end -}}

{{/*
Multus annotation attaching the postgres pod to the CUDN-managed NAD.
Uses simple <namespace>/<name> format — static IP is driven by the
k8s.ovn.org/pod-networks pre-annotation, not by the Multus ips field
(which OVN-K does not support for secondary Layer2 UDN networks).
*/}}
{{- define "qtodo-db.udn.networksAnnotation" -}}
{{ .Release.Namespace }}/{{ .Values.udn.name }}
{{- end -}}

{{/*
OVN-K pod-networks pre-annotation that requests a static IP on the
secondary UDN. The key format is <namespace>/<nad-name> — matching the
key OVN-K writes when it allocates dynamically. OVN-K reads this before
the CNI call and honours the specified ip_addresses instead of allocating
a new one from the pool.
*/}}
{{- define "qtodo-db.udn.ovnPodNetworks" -}}
{{- $ip := printf "%s/%s" .Values.udn.dbIP (include "qtodo-db.udn.prefixlen" .) -}}
{{- $key := printf "%s/%s" .Release.Namespace .Values.udn.name -}}
{"{{ $key }}":{"ip_addresses":["{{ $ip }}"],"ip_address":"{{ $ip }}","role":"secondary"}}
{{- end -}}
5 changes: 5 additions & 0 deletions charts/qtodo-db/templates/postgresql-statefulset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ spec:
serviceName: qtodo-db
template:
metadata:
{{- if include "qtodo-db.udn.enabled" . }}
annotations:
k8s.v1.cni.cncf.io/networks: {{ include "qtodo-db.udn.networksAnnotation" . | quote }}
k8s.ovn.org/pod-networks: {{ include "qtodo-db.udn.ovnPodNetworks" . | quote }}
{{- end }}
labels:
app: qtodo-db
spec:
Expand Down
10 changes: 8 additions & 2 deletions charts/qtodo-db/templates/qtodo-db-network-policy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ spec:
policyTypes:
- Ingress
- Egress
{{- if eq (include "qtodo-db.udn.enabled" .) "" }}
ingress:
# PostgreSQL — only from qtodo app pods in the qtodo namespace
# PostgreSQL — only from qtodo app pods in the qtodo namespace (cluster network)
- ports:
- protocol: TCP
port: {{ .Values.postgresql.port }}
Expand All @@ -22,8 +23,13 @@ spec:
podSelector:
matchLabels:
app: {{ .Values.postgresql.allowedClientApp }}
{{- end }}
# When UDN is enabled there is no ingress allow-list: default-deny plus
# policyTypes: Ingress blocks PostgreSQL on the cluster network. Clients
# must use the shared secondary UDN (see udn-multi-network-policy.yaml).
egress:
# DNS resolution via CoreDNS — OCP uses port 5353 (not 53)
# DNS resolution via CoreDNS — OCP uses port 5353 (not 53). This is the
# only egress qtodo-db is permitted on the cluster network.
- ports:
- protocol: UDP
port: 5353
Expand Down
38 changes: 38 additions & 0 deletions charts/qtodo-db/templates/udn-cluster-user-defined-network.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{{- if include "qtodo-db.udn.enabled" . }}
{{- if ne .Values.udn.topology "Layer2" }}
{{- fail "qtodo-db UDN supports Layer2 topology only (shared secondary ClusterUserDefinedNetwork)" }}
{{- end }}
{{- if ne .Values.udn.role "Secondary" }}
{{- fail "qtodo-db UDN must use role Secondary so qtodo can keep the cluster network as its primary interface" }}
{{- end }}
# Cluster-scoped Layer2 secondary UDN shared by the qtodo and qtodo-db
# namespaces. OVN-Kubernetes creates a NetworkAttachmentDefinition of the
# same name in each selected namespace; pods opt in via the
# k8s.v1.cni.cncf.io/networks annotation. reservedSubnets is not used here
# because it is only valid for Primary networks.
apiVersion: k8s.ovn.org/v1
kind: ClusterUserDefinedNetwork
metadata:
annotations:
argocd.argoproj.io/sync-wave: "35"
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
name: {{ .Values.udn.name }}
spec:
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: In
values:
{{- range .Values.udn.namespaces }}
- {{ . | quote }}
{{- end }}
network:
topology: {{ .Values.udn.topology }}
layer2:
role: {{ .Values.udn.role }}
subnets:
- {{ .Values.udn.subnet }}
{{- if .Values.udn.mtu }}
mtu: {{ .Values.udn.mtu }}
{{- end }}
{{- end }}
48 changes: 48 additions & 0 deletions charts/qtodo-db/templates/udn-multi-network-policy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{{- if and (include "qtodo-db.udn.enabled" .) .Values.udn.networkPolicy.enabled }}
# Default deny on the secondary UDN, then allow PostgreSQL only from qtodo.
# MultiNetworkPolicy applies to the CUDN-managed NAD, not the cluster network.
# Requires spec.useMultiNetworkPolicy: true on network.operator.openshift.io/cluster.
apiVersion: k8s.cni.cncf.io/v1beta1
kind: MultiNetworkPolicy
metadata:
annotations:
argocd.argoproj.io/sync-wave: "37"
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
k8s.v1.cni.cncf.io/policy-for: {{ .Release.Namespace }}/{{ .Values.udn.name }}
name: default-deny-udn
namespace: {{ .Release.Namespace }}
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress: []
egress: []
---
apiVersion: k8s.cni.cncf.io/v1beta1
kind: MultiNetworkPolicy
metadata:
annotations:
argocd.argoproj.io/sync-wave: "37"
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
k8s.v1.cni.cncf.io/policy-for: {{ .Release.Namespace }}/{{ .Values.udn.name }}
name: allow-qtodo-postgresql
namespace: {{ .Release.Namespace }}
spec:
podSelector:
matchLabels:
app: qtodo-db
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: {{ .Values.postgresql.allowedClientNamespace }}
podSelector:
matchLabels:
app: {{ .Values.postgresql.allowedClientApp }}
ports:
- protocol: TCP
port: {{ .Values.udn.networkPolicy.postgresqlPort | default .Values.postgresql.port }}
{{- end }}
24 changes: 23 additions & 1 deletion charts/qtodo-db/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,28 @@ postgresql:
# qtodo DB password path (app-level isolation)
passwordVaultKey: secret/data/apps/qtodo/qtodo-db

# Namespace allowed to connect to PostgreSQL
# Namespace allowed to connect to PostgreSQL on the cluster network.
# Ignored when udn.enabled is true — PostgreSQL is then reachable only
# on the shared secondary UDN (see udn.dbIP).
allowedClientNamespace: qtodo
allowedClientApp: qtodo

# Shared secondary User-Defined Network (CUDN) spanning qtodo and qtodo-db.
# Keep name, subnet, and dbIP in sync with charts/qtodo values (app.udn.*).
# Kubernetes Services are not supported on secondary UDNs, so qtodo connects
# to PostgreSQL by the static UDN IP set via network annotation (udn.dbIP).
udn:
enabled: false
name: qtodo-isolated-network
topology: Layer2
role: Secondary
subnet: "10.100.0.0/16"
mtu: 1400
dbIP: "10.100.0.10"
namespaces:
- qtodo
- qtodo-db
networkPolicy:
# MultiNetworkPolicy on the UDN (requires CNO useMultiNetworkPolicy: true)
enabled: true
postgresqlPort: 5432
21 changes: 21 additions & 0 deletions charts/qtodo/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,25 @@ Returns the port the application should list on
{{- else -}}
{{ .Values.app.insecurePort }}
{{- end -}}
{{- end -}}

{{/*
True when the shared secondary UDN is enabled (bool or string "true").
*/}}
{{- define "qtodo.udn.enabled" -}}
{{- if eq (.Values.app.udn.enabled | default false | toString) "true" -}}
true
{{- end -}}
{{- end -}}

{{/*
PostgreSQL host: static UDN IP when UDN is enabled (secondary UDNs do not
support Kubernetes Services), otherwise the cluster-network Service DNS name.
*/}}
{{- define "qtodo.dbHost" -}}
{{- if include "qtodo.udn.enabled" . -}}
{{- .Values.app.udn.dbIP -}}
{{- else -}}
{{- .Values.postgresql.host -}}
{{- end -}}
{{- end -}}
5 changes: 4 additions & 1 deletion charts/qtodo/templates/app-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ spec:
{{- if .Values.app.spire.enabled }}
checksum/app-spiffe-helper-config: {{ include (print $.Template.BasePath "/spiffe-helper-config.yaml") . | sha256sum }}
checksum/app-spiffe-vault-client-config: {{ include (print $.Template.BasePath "/spiffe-vault-client-config.yaml") . | sha256sum }}
{{- end }}
{{- if include "qtodo.udn.enabled" . }}
k8s.v1.cni.cncf.io/networks: {{ .Release.Namespace }}/{{ .Values.app.udn.name }}
{{- end }}
labels:
app: qtodo
Expand Down Expand Up @@ -215,7 +218,7 @@ spec:
type: RuntimeDefault
env:
- name: QUARKUS_DATASOURCE_JDBC_URL
value: 'jdbc:postgresql://{{ .Values.postgresql.host }}:{{ .Values.postgresql.port }}/{{ .Values.postgresql.auth.database }}'
value: 'jdbc:postgresql://{{ include "qtodo.dbHost" . }}:{{ .Values.postgresql.port }}/{{ .Values.postgresql.auth.database }}'
- name: QUARKUS_HTTP_HOST
value: '0.0.0.0'
- name: QUARKUS_HTTP_PORT
Expand Down
7 changes: 6 additions & 1 deletion charts/qtodo/templates/qtodo-network-policy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ spec:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: openshift-dns
# PostgreSQL — qtodo-db pod in the qtodo-db namespace
# When UDN is enabled, PostgreSQL is reached on the secondary UDN (net1)
# using the static IP in app.udn.dbIP. Cluster-network egress to 5432 is
# intentionally omitted so the database path cannot bypass the UDN.
{{- if eq (include "qtodo.udn.enabled" .) "" }}
# PostgreSQL — qtodo-db pod in the qtodo-db namespace (cluster network)
- ports:
- protocol: TCP
port: {{ .Values.postgresql.port }}
Expand All @@ -44,6 +48,7 @@ spec:
podSelector:
matchLabels:
app: qtodo-db
{{- end }}
# Vault API — SPIFFE JWT auth for DB credentials retrieval
- ports:
- protocol: TCP
Expand Down
65 changes: 65 additions & 0 deletions charts/qtodo/templates/udn-enable-multi-network-policy-job.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{{- if and (include "qtodo.udn.enabled" .) .Values.app.udn.networkPolicy.enabled }}
# Enables spec.useMultiNetworkPolicy on network.operator.openshift.io/cluster
# before MultiNetworkPolicy objects sync at wave 37.
# Runs in the default namespace (no NetworkPolicies) because this is a cluster-wide change.
apiVersion: batch/v1
kind: Job
metadata:
name: enable-multi-network-policy
namespace: {{ .Values.app.udn.multiNetworkPolicyJob.namespace }}
labels:
app: enable-multi-network-policy
annotations:
argocd.argoproj.io/sync-wave: "36"
argocd.argoproj.io/hook: Sync
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
spec:
template:
metadata:
name: enable-multi-network-policy
labels:
app: enable-multi-network-policy
spec:
serviceAccountName: enable-multi-network-policy
restartPolicy: OnFailure
containers:
- name: enable-multi-network-policy
image: {{ .Values.app.udn.multiNetworkPolicyJob.image.registry }}/{{ .Values.app.udn.multiNetworkPolicyJob.image.repository }}:{{ .Values.app.udn.multiNetworkPolicyJob.image.tag }}
imagePullPolicy: {{ .Values.app.udn.multiNetworkPolicyJob.image.pullPolicy }}
command:
- /bin/bash
- -c
- |
#!/usr/bin/env bash
set -euo pipefail

CURRENT="$(oc get network.operator.openshift.io cluster \
-o jsonpath='{.spec.useMultiNetworkPolicy}' 2>/dev/null || true)"

if [[ "${CURRENT}" == "true" ]]; then
echo "spec.useMultiNetworkPolicy is already true — no patch needed"
else
echo "Enabling spec.useMultiNetworkPolicy on network.operator.openshift.io/cluster"
oc patch network.operator.openshift.io cluster --type merge \
-p '{"spec":{"useMultiNetworkPolicy":true}}'
echo "Patch applied successfully"
fi

{{- if .Values.app.udn.multiNetworkPolicyJob.waitForReconciliation.enabled }}
MAX_RETRIES={{ .Values.app.udn.multiNetworkPolicyJob.waitForReconciliation.maxRetries }}
INTERVAL={{ .Values.app.udn.multiNetworkPolicyJob.waitForReconciliation.intervalSeconds }}
for i in $(seq 1 "${MAX_RETRIES}"); do
if oc api-resources --api-group=k8s.cni.cncf.io 2>/dev/null | grep -q multi-networkpolicies; then
echo "MultiNetworkPolicy API is available (attempt ${i}/${MAX_RETRIES})"
exit 0
fi
echo "Waiting for CNO to reconcile MultiNetworkPolicy API (${i}/${MAX_RETRIES})..."
sleep "${INTERVAL}"
done
echo "ERROR: timeout waiting for MultiNetworkPolicy API after patch"
exit 1
{{- else }}
exit 0
{{- end }}
terminationGracePeriodSeconds: 30
{{- end }}
43 changes: 43 additions & 0 deletions charts/qtodo/templates/udn-enable-multi-network-policy-rbac.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{{- if and (include "qtodo.udn.enabled" .) .Values.app.udn.networkPolicy.enabled }}
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: enable-multi-network-policy
namespace: {{ .Values.app.udn.multiNetworkPolicyJob.namespace }}
labels:
app: enable-multi-network-policy
annotations:
argocd.argoproj.io/sync-wave: "35"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: enable-multi-network-policy
labels:
app: enable-multi-network-policy
annotations:
argocd.argoproj.io/sync-wave: "35"
rules:
- apiGroups: ["operator.openshift.io"]
resources: ["networks"]
resourceNames: ["cluster"]
verbs: ["get", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: enable-multi-network-policy
labels:
app: enable-multi-network-policy
annotations:
argocd.argoproj.io/sync-wave: "35"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: enable-multi-network-policy
subjects:
- kind: ServiceAccount
name: enable-multi-network-policy
namespace: {{ .Values.app.udn.multiNetworkPolicyJob.namespace }}
{{- end }}
Loading
Loading