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
1 change: 1 addition & 0 deletions .github/workflows/helm-kustomize-comparison.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
pull_request:
paths:
- experimental/helm/charts/**
- common/cert-manager/helm/**
- tests/kustomize_install.sh
- tests/helm_kustomize_compare.py
- tests/helm_kustomize_compare.sh
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@


# Scanning reports of trivy
image_lists/*
image_lists/*

# Helm dependency archives generated during local wrapper-chart validation.
experimental/helm/charts/cert-manager/charts/*.tgz
1 change: 1 addition & 0 deletions .yamllint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ ignore: |
common/istio/cluster-local-gateway/base/cluster-local-gateway.yaml
common/istio/istio-crds/base/crd.yaml
common/istio/istio-install/base/install.yaml
common/*/helm/templates/**
**/upstream/**

rules:
Expand Down
5 changes: 5 additions & 0 deletions common/cert-manager/helm/.helmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.git/
.gitignore
.DS_Store
*.swp
*.tmp
6 changes: 6 additions & 0 deletions common/cert-manager/helm/Chart.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
dependencies:
- name: cert-manager
repository: https://charts.jetstack.io
version: v1.20.2
digest: sha256:191ee84cea4b12c4f3be2c9a01b73f09cae6a3b5a3b76bb35e76359f1b2153b2
generated: "2026-06-15T10:40:10.97574309+05:30"
21 changes: 21 additions & 0 deletions common/cert-manager/helm/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: v2
name: cert-manager
description: A Helm wrapper chart for cert-manager with Kubeflow platform defaults
version: 0.1.0
appVersion: v1.20.2
keywords:
- cert-manager
- kubeflow
- certificates
home: https://github.com/kubeflow/manifests
sources:
- https://cert-manager.io
- https://github.com/kubeflow/manifests
dependencies:
- name: cert-manager
version: "1.20.2"
repository: https://charts.jetstack.io
condition: cert-manager.enabled
annotations:
category: Security
licenses: Apache-2.0
50 changes: 50 additions & 0 deletions common/cert-manager/helm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Cert Manager Helm Wrapper Chart

This chart wraps the upstream cert-manager Helm chart and adds the Kubeflow-specific cert-manager resources from `common/cert-manager/overlays/kubeflow`.

It installs:

- upstream cert-manager `v1.20.2`
- cert-manager CRDs
- optional `ClusterIssuer/kubeflow-self-signing-issuer`
- optional Kubeflow-specific cert-manager NetworkPolicies

In the Kubeflow platform install, apply the foundation charts first:

| Chart | Provides |
| --- | --- |
| `kubeflow-namespaces` | `Namespace/cert-manager` and other platform namespaces |
| `kubeflow-platform` | shared Kubeflow platform RBAC |

This wrapper stores its Helm release metadata in the `cert-manager` workload namespace.

## Namespace names

The cert-manager workload namespace is fixed to `cert-manager` to match the Kustomize baseline and `kubeflow-namespaces` foundation chart. It is not configurable.

```bash
helm install kubeflow-namespaces ./experimental/helm/charts/kubeflow-namespaces --namespace default
helm install kubeflow-platform ./experimental/helm/charts/kubeflow-platform --namespace kubeflow-system

helm dep build ./common/cert-manager/helm
helm install cert-manager ./common/cert-manager/helm --namespace cert-manager --wait
helm upgrade cert-manager ./common/cert-manager/helm --namespace cert-manager \
--values ./common/cert-manager/helm/ci/values-kubeflow.yaml --wait
```

The install is split into base install plus upgrade because `ClusterIssuer` cannot be created until cert-manager CRDs are available.

If the cluster already has a company-managed cert-manager installation, disable the upstream dependency, then install only the Kubeflow-specific resources. If `Namespace/cert-manager` already exists, the foundation chart does not recreate or adopt it; apply any required labels separately if they are missing.

```bash
helm install cert-manager ./common/cert-manager/helm --namespace cert-manager \
--values ./common/cert-manager/helm/ci/values-existing-cert-manager.yaml --wait
```

In this mode, cert-manager CRDs, webhook, and controllers must already exist.

Validate parity with:

```bash
./tests/helm_kustomize_compare.sh cert-manager base
```
14 changes: 14 additions & 0 deletions common/cert-manager/helm/ci/values-base.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# CI values for upstream cert-manager base parity.
cert-manager:
enabled: true
crds:
enabled: true
startupapicheck:
enabled: false

kubeflow:
enabled: false
clusterIssuer:
enabled: false
networkPolicies:
enabled: false
11 changes: 11 additions & 0 deletions common/cert-manager/helm/ci/values-existing-cert-manager.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# CI values for Kubeflow cert-manager resources with an externally managed cert-manager.
cert-manager:
enabled: false

kubeflow:
enabled: true
clusterIssuer:
enabled: true
name: kubeflow-self-signing-issuer
networkPolicies:
enabled: true
15 changes: 15 additions & 0 deletions common/cert-manager/helm/ci/values-kubeflow.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# CI values for Kubeflow cert-manager parity.
cert-manager:
enabled: true
crds:
enabled: true
startupapicheck:
enabled: false

kubeflow:
enabled: true
clusterIssuer:
enabled: true
name: kubeflow-self-signing-issuer
networkPolicies:
enabled: true
54 changes: 54 additions & 0 deletions common/cert-manager/helm/templates/kubeflow-resources.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{{ if .Values.kubeflow.enabled }}
{{ if .Values.kubeflow.clusterIssuer.enabled }}
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: {{ .Values.kubeflow.clusterIssuer.name | quote }}
labels:
{{- toYaml .Values.kubeflow.labels | nindent 4 }}
spec:
selfSigned: {}
{{ end }}
{{ if .Values.kubeflow.networkPolicies.enabled }}
---
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: cert-manager-webhook
namespace: cert-manager
labels:
{{- toYaml .Values.kubeflow.labels | nindent 4 }}
spec:
podSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- webhook
- key: app.kubernetes.io/component
operator: In
values:
- "webhook"
ingress:
- ports:
- protocol: TCP
port: 10250
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-allow-same-namespace-cert-manager
namespace: cert-manager
labels:
{{- toYaml .Values.kubeflow.labels | nindent 4 }}
spec:
podSelector: {}
ingress:
- from:
- podSelector: {}
policyTypes:
- Ingress
{{ end }}
{{ end }}
26 changes: 26 additions & 0 deletions common/cert-manager/helm/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Default values for the Kubeflow cert-manager wrapper chart.

cert-manager:
enabled: true
crds:
enabled: true
startupapicheck:
enabled: false

kubeflow:
# -- Enable Kubeflow-specific cert-manager resources.
enabled: false

clusterIssuer:
# -- Create the Kubeflow self-signed ClusterIssuer.
enabled: true
name: kubeflow-self-signing-issuer

networkPolicies:
# -- Create Kubeflow cert-manager NetworkPolicies.
enabled: true

labels:
app.kubernetes.io/component: cert-manager
app.kubernetes.io/name: cert-manager
kustomize.component: cert-manager
53 changes: 48 additions & 5 deletions tests/helm_kustomize_compare.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@
from typing import Dict, List, Tuple, Any
import re

CERT_MANAGER_KUBEFLOW_RESOURCES = {
("ClusterIssuer", "kubeflow-self-signing-issuer"),
("NetworkPolicy", "cert-manager-webhook"),
("NetworkPolicy", "default-allow-same-namespace-cert-manager"),
}

CERT_MANAGER_KUBEFLOW_LABELS = {
"app.kubernetes.io/component",
"app.kubernetes.io/name",
}

def load_manifests(file_path: str) -> List[Dict]:
"""Load YAML manifests from file."""
with open(file_path, 'r') as f:
Expand Down Expand Up @@ -110,6 +121,9 @@ def normalize_manifest(manifest: Dict, component: str = "katib") -> Dict:

# Clean Helm-specific metadata
normalized = clean_helm_metadata(normalized, component)

if component == "cert-manager":
preserve_cert_manager_kubeflow_labels(manifest, normalized)

# Normalize Kustomize hash references
normalized = normalize_kustomize_refs(normalized)
Expand Down Expand Up @@ -158,6 +172,31 @@ def remove_empty_values(obj):

return remove_empty_values(normalized)

def preserve_cert_manager_kubeflow_labels(original: Dict, normalized: Dict) -> None:
"""Keep labels that are intentionally added by cert-manager's Kubeflow overlay."""
kind = original.get("kind", "")
name = original.get("metadata", {}).get("name", "")

if (kind, name) not in CERT_MANAGER_KUBEFLOW_RESOURCES:
return

labels = original.get("metadata", {}).get("labels", {})
preserved_labels = {
key: value
for key, value in labels.items()
if key in CERT_MANAGER_KUBEFLOW_LABELS
}

if preserved_labels:
normalized.setdefault("metadata", {}).setdefault("labels", {}).update(preserved_labels)

def should_compare_manifest(manifest: Dict, component: str, scenario: str) -> bool:
"""Select the resource subset owned by a comparison scenario."""
if component == "cert-manager" and manifest.get("kind") == "Namespace":
return False

return True

def get_resource_key(manifest: Dict, component: str = "katib") -> str:
"""Generate a unique key for the resource."""
kind = manifest.get('kind', 'Unknown')
Expand All @@ -167,8 +206,8 @@ def get_resource_key(manifest: Dict, component: str = "katib") -> str:
if kind in ['Secret', 'ConfigMap']:
name = re.sub(r'-[a-z0-9]{10}$', '', name)

# Include namespace in key only for Katib
if component == "katib" and namespace:
# Include namespace in key for components that render resources across multiple namespaces.
if component in ["katib", "cert-manager"] and namespace:
return f"{kind}/{namespace}/{name}"
else:
return f"{kind}/{name}"
Expand Down Expand Up @@ -226,11 +265,15 @@ def compare_manifests(kustomize_file: str, helm_file: str, component: str, scena
helm_resources = {}

for manifest in kustomize_manifests:
if not should_compare_manifest(manifest, component, scenario):
continue
normalized = normalize_manifest(manifest, component)
key = get_resource_key(normalized, component)
kustomize_resources[key] = normalized

for manifest in helm_manifests:
if not should_compare_manifest(manifest, component, scenario):
continue
normalized = normalize_manifest(manifest, component)
key = get_resource_key(normalized, component)
helm_resources[key] = normalized
Expand Down Expand Up @@ -279,7 +322,7 @@ def compare_manifests(kustomize_file: str, helm_file: str, component: str, scena
if __name__ == "__main__":
if len(sys.argv) < 5:
print("Usage: python compare.py <kustomize_file> <helm_file> <component> <scenario> [namespace] [--verbose]")
print("Components: katib, hub, kserve-models-web-app")
print("Components: katib, hub, kserve-models-web-app, cert-manager")
sys.exit(1)

kustomize_file = sys.argv[1]
Expand All @@ -288,9 +331,9 @@ def compare_manifests(kustomize_file: str, helm_file: str, component: str, scena
scenario = sys.argv[4]
namespace = sys.argv[5] if len(sys.argv) > 5 and not sys.argv[5].startswith('--') else ""

if component not in ["katib", "hub", "kserve-models-web-app"]:
if component not in ["katib", "hub", "kserve-models-web-app", "cert-manager"]:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed by preserving the Kubeflow cert-manager overlay labels on the overlay-owned resources during comparison.

print(f"ERROR: Unknown component: {component}")
print("Supported components: katib, hub, kserve-models-web-app")
print("Supported components: katib, hub, kserve-models-web-app, cert-manager")
sys.exit(1)

success = compare_manifests(kustomize_file, helm_file, component, scenario, namespace)
Expand Down
29 changes: 27 additions & 2 deletions tests/helm_kustomize_compare.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ ROOT_DIR="$(dirname "$SCRIPT_DIR")"
if [[ -z "$COMPONENT" ]]; then
echo "ERROR: Component is required"
echo "Usage: $0 <component> <scenario>"
echo "Components: katib, hub, kserve-models-web-app"
echo "Components: katib, hub, kserve-models-web-app, cert-manager"
exit 1
fi

Expand Down Expand Up @@ -132,9 +132,26 @@ case "$COMPONENT" in
)
;;

"cert-manager")
CHART_DIR="$ROOT_DIR/common/cert-manager/helm"
MANIFESTS_DIR="$ROOT_DIR/common/cert-manager"

declare -A KUSTOMIZE_PATHS=(
["base"]="$MANIFESTS_DIR/base"
)

declare -A HELM_VALUES=(
["base"]="$CHART_DIR/ci/values-base.yaml"
)

declare -A NAMESPACES=(
["base"]="cert-manager"
)
;;

*)
echo "ERROR: Unknown component: $COMPONENT"
echo "Supported components: katib, hub, kserve-models-web-app"
echo "Supported components: katib, hub, kserve-models-web-app, cert-manager"
exit 1
;;
esac
Expand Down Expand Up @@ -187,6 +204,14 @@ if [[ "$COMPONENT" == "kserve-models-web-app" ]]; then
helm template kserve-models-web-application "$CHART_DIR" \
--namespace "$NAMESPACE" > "$HELM_OUTPUT"
fi
elif [[ "$COMPONENT" == "cert-manager" ]]; then
cd "$CHART_DIR"
helm repo add jetstack https://charts.jetstack.io >/dev/null 2>&1 || helm repo update jetstack >/dev/null
helm dependency build .
helm template cert-manager . \
--namespace "$NAMESPACE" \
--include-crds \
--values "$HELM_VALUES_ARG" > "$HELM_OUTPUT"
Comment on lines +207 to +214
else
cd "$CHART_DIR"
if [[ "$COMPONENT" == "katib" ]]; then
Expand Down
Loading
Loading