From b9e8d3ea3500661f3c1823137165c5398bd6844c Mon Sep 17 00:00:00 2001 From: OAharoni-RedHat Date: Mon, 13 Jul 2026 18:14:35 -0400 Subject: [PATCH 1/5] test new CA changes --- .../files/odf-dr-prerequisites-check.sh | 92 +++++--------- .../files/odf-ramen-trusted-ca.sh | 17 +-- .../files/odf-ssl-ramen-hub-configmap.sh | 114 ++++++++++++------ templates/configmap-odf-ssl-playbooks.yaml | 2 + .../job-odf-ssl-certificate-extraction.yaml | 2 + .../job-odf-ssl-certificate-precheck.yaml | 2 + templates/placement-binding-odf-ssl-hub.yaml | 2 + templates/placement-binding-odf-ssl.yaml | 2 + templates/placement-odf-ssl-certificates.yaml | 2 + templates/placement-odf-ssl-hub.yaml | 2 + ...policy-odf-ssl-certificate-management.yaml | 2 +- .../rbac-odf-ssl-certificate-precheck.yaml | 4 +- templates/rbac-odf-ssl-extractor.yaml | 2 + values.yaml | 7 +- 14 files changed, 138 insertions(+), 114 deletions(-) diff --git a/ansible/roles/odf_dr_prerequisites/files/odf-dr-prerequisites-check.sh b/ansible/roles/odf_dr_prerequisites/files/odf-dr-prerequisites-check.sh index 9f87832..a45d13b 100755 --- a/ansible/roles/odf_dr_prerequisites/files/odf-dr-prerequisites-check.sh +++ b/ansible/roles/odf_dr_prerequisites/files/odf-dr-prerequisites-check.sh @@ -251,24 +251,24 @@ check_ca_configuration() { echo "Checking CA configuration on $cluster..." - # Check if cluster-proxy-ca-bundle ConfigMap exists - if ! oc --kubeconfig="$kubeconfig" get configmap cluster-proxy-ca-bundle -n openshift-config &>/dev/null; then - report_check_failure "CA config ($cluster): ConfigMap cluster-proxy-ca-bundle not found in openshift-config" + # Check if vp-pattern-proxy-ca-bundle ConfigMap exists + if ! oc --kubeconfig="$kubeconfig" get configmap vp-pattern-proxy-ca-bundle -n openshift-config &>/dev/null; then + report_check_failure "CA config ($cluster): ConfigMap vp-pattern-proxy-ca-bundle not found in openshift-config" return 1 fi # Check if ConfigMap has certificate data - local ca_bundle_size=$(oc --kubeconfig="$kubeconfig" get configmap cluster-proxy-ca-bundle -n openshift-config -o jsonpath='{.data.ca-bundle\.crt}' 2>/dev/null | wc -c || echo "0") + local ca_bundle_size=$(oc --kubeconfig="$kubeconfig" get configmap vp-pattern-proxy-ca-bundle -n openshift-config -o jsonpath='{.data.ca-bundle\.crt}' 2>/dev/null | wc -c || echo "0") ca_bundle_size=$(echo "$ca_bundle_size" | tr -d ' \n') if [[ $ca_bundle_size -lt 100 ]]; then - report_check_failure "CA config ($cluster): cluster-proxy-ca-bundle data ca-bundle.crt too small or empty (bytes: $ca_bundle_size)" + report_check_failure "CA config ($cluster): vp-pattern-proxy-ca-bundle data ca-bundle.crt too small or empty (bytes: $ca_bundle_size)" return 1 fi # Check if Proxy object is configured local proxy_trusted_ca=$(oc --kubeconfig="$kubeconfig" get proxy cluster -o jsonpath='{.spec.trustedCA.name}' 2>/dev/null || echo "") - if [[ "$proxy_trusted_ca" != "cluster-proxy-ca-bundle" ]]; then - report_check_failure "CA config ($cluster): Proxy cluster spec.trustedCA.name is '$proxy_trusted_ca' (expected cluster-proxy-ca-bundle)" + if [[ "$proxy_trusted_ca" != "vp-pattern-proxy-ca-bundle" ]]; then + report_check_failure "CA config ($cluster): Proxy cluster spec.trustedCA.name is '$proxy_trusted_ca' (expected vp-pattern-proxy-ca-bundle)" return 1 fi @@ -285,9 +285,9 @@ check_ca_material_completeness() { echo "Checking CA material completeness across all clusters..." # Extract CA bundle from each cluster - local hub_ca_bundle=$(oc --kubeconfig="$hub_kubeconfig" get configmap cluster-proxy-ca-bundle -n openshift-config -o jsonpath='{.data.ca-bundle\.crt}' 2>/dev/null || echo "") - local primary_ca_bundle=$(oc --kubeconfig="$primary_kubeconfig" get configmap cluster-proxy-ca-bundle -n openshift-config -o jsonpath='{.data.ca-bundle\.crt}' 2>/dev/null || echo "") - local secondary_ca_bundle=$(oc --kubeconfig="$secondary_kubeconfig" get configmap cluster-proxy-ca-bundle -n openshift-config -o jsonpath='{.data.ca-bundle\.crt}' 2>/dev/null || echo "") + local hub_ca_bundle=$(oc --kubeconfig="$hub_kubeconfig" get configmap vp-pattern-proxy-ca-bundle -n openshift-config -o jsonpath='{.data.ca-bundle\.crt}' 2>/dev/null || echo "") + local primary_ca_bundle=$(oc --kubeconfig="$primary_kubeconfig" get configmap vp-pattern-proxy-ca-bundle -n openshift-config -o jsonpath='{.data.ca-bundle\.crt}' 2>/dev/null || echo "") + local secondary_ca_bundle=$(oc --kubeconfig="$secondary_kubeconfig" get configmap vp-pattern-proxy-ca-bundle -n openshift-config -o jsonpath='{.data.ca-bundle\.crt}' 2>/dev/null || echo "") # Check if all CA bundles exist and have reasonable size if [[ -z "$hub_ca_bundle" || ${#hub_ca_bundle} -lt 100 ]]; then @@ -305,77 +305,41 @@ check_ca_material_completeness() { return 1 fi - # Check if all CA bundles contain certificates from all three clusters - echo "๐Ÿ” Debug: Checking CA bundle contents..." - echo "Hub CA bundle size: ${#hub_ca_bundle} characters" - echo "Primary CA bundle size: ${#primary_ca_bundle} characters" - echo "Secondary CA bundle size: ${#secondary_ca_bundle} characters" - echo "Hub CA bundle first 500 chars:" - echo "${hub_ca_bundle:0:500}" - echo "" - - # Look for hub cluster certificates - if [[ "$hub_ca_bundle" != *"# CA from hub-ca"* ]]; then - echo "Available markers in hub CA bundle:" - echo "$hub_ca_bundle" | grep "^# CA from" || echo "No CA markers found" - report_check_failure "CA material: hub bundle missing marker '# CA from hub-ca'" - return 1 - fi - - if [[ "$primary_ca_bundle" != *"# CA from hub-ca"* ]]; then - echo "Available markers in primary CA bundle:" - echo "$primary_ca_bundle" | grep "^# CA from" || echo "No CA markers found" - report_check_failure "CA material: primary bundle missing marker '# CA from hub-ca'" - return 1 - fi - - if [[ "$secondary_ca_bundle" != *"# CA from hub-ca"* ]]; then - echo "Available markers in secondary CA bundle:" - echo "$secondary_ca_bundle" | grep "^# CA from" || echo "No CA markers found" - report_check_failure "CA material: secondary bundle missing marker '# CA from hub-ca'" - return 1 - fi + # Check that each bundle contains PEM certificate data. + # Note: vp-manage-proxy-cluster-ca writes standard PEM without custom markers. + local MIN_CERTS=3 + local hub_cert_count primary_cert_count secondary_cert_count + hub_cert_count=$(echo "$hub_ca_bundle" | grep -c "BEGIN CERTIFICATE" || echo "0") + primary_cert_count=$(echo "$primary_ca_bundle" | grep -c "BEGIN CERTIFICATE" || echo "0") + secondary_cert_count=$(echo "$secondary_ca_bundle" | grep -c "BEGIN CERTIFICATE" || echo "0") - # Look for primary cluster certificates (marker from odf-ssl-certificate-extraction.sh) - if [[ "$hub_ca_bundle" != *"# CA from ${PRIMARY_CLUSTER}-ca"* ]]; then - report_check_failure "CA material: hub bundle missing marker '# CA from ${PRIMARY_CLUSTER}-ca'" - return 1 - fi - - if [[ "$primary_ca_bundle" != *"# CA from ${PRIMARY_CLUSTER}-ca"* ]]; then - report_check_failure "CA material: primary bundle missing marker '# CA from ${PRIMARY_CLUSTER}-ca'" - return 1 - fi - - if [[ "$secondary_ca_bundle" != *"# CA from ${PRIMARY_CLUSTER}-ca"* ]]; then - report_check_failure "CA material: secondary bundle missing marker '# CA from ${PRIMARY_CLUSTER}-ca'" - return 1 - fi + echo "Hub CA bundle: ${#hub_ca_bundle} chars, ${hub_cert_count} certificates" + echo "Primary CA bundle: ${#primary_ca_bundle} chars, ${primary_cert_count} certificates" + echo "Secondary CA bundle: ${#secondary_ca_bundle} chars, ${secondary_cert_count} certificates" - # Look for secondary cluster certificates - if [[ "$hub_ca_bundle" != *"# CA from ${SECONDARY_CLUSTER}-ca"* ]]; then - report_check_failure "CA material: hub bundle missing marker '# CA from ${SECONDARY_CLUSTER}-ca'" + if [[ "$hub_cert_count" -lt "$MIN_CERTS" ]]; then + report_check_failure "CA material: hub bundle has only ${hub_cert_count} certificates (expected at least ${MIN_CERTS})" return 1 fi - if [[ "$primary_ca_bundle" != *"# CA from ${SECONDARY_CLUSTER}-ca"* ]]; then - report_check_failure "CA material: primary bundle missing marker '# CA from ${SECONDARY_CLUSTER}-ca'" + if [[ "$primary_cert_count" -lt "$MIN_CERTS" ]]; then + report_check_failure "CA material: primary ($PRIMARY_CLUSTER) bundle has only ${primary_cert_count} certificates (expected at least ${MIN_CERTS})" return 1 fi - if [[ "$secondary_ca_bundle" != *"# CA from ${SECONDARY_CLUSTER}-ca"* ]]; then - report_check_failure "CA material: secondary bundle missing marker '# CA from ${SECONDARY_CLUSTER}-ca'" + if [[ "$secondary_cert_count" -lt "$MIN_CERTS" ]]; then + report_check_failure "CA material: secondary ($SECONDARY_CLUSTER) bundle has only ${secondary_cert_count} certificates (expected at least ${MIN_CERTS})" return 1 fi # Check that all CA bundles are identical (they should contain the same combined certificate data) if [[ "$hub_ca_bundle" != "$primary_ca_bundle" ]]; then - report_check_failure "CA material: hub and primary cluster-proxy-ca-bundle contents differ (must be identical after trust sync)" + report_check_failure "CA material: hub and primary vp-pattern-proxy-ca-bundle contents differ (must be identical after trust sync)" return 1 fi if [[ "$hub_ca_bundle" != "$secondary_ca_bundle" ]]; then - report_check_failure "CA material: hub and secondary cluster-proxy-ca-bundle contents differ (must be identical after trust sync)" + report_check_failure "CA material: hub and secondary vp-pattern-proxy-ca-bundle contents differ (must be identical after trust sync)" return 1 fi diff --git a/ansible/roles/odf_ramen_trusted_ca/files/odf-ramen-trusted-ca.sh b/ansible/roles/odf_ramen_trusted_ca/files/odf-ramen-trusted-ca.sh index 021c036..73be1d3 100755 --- a/ansible/roles/odf_ramen_trusted_ca/files/odf-ramen-trusted-ca.sh +++ b/ansible/roles/odf_ramen_trusted_ca/files/odf-ramen-trusted-ca.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# After MirrorPeer (and policies that populate cluster-proxy-ca-bundle), copy CA from the hub -# cluster-proxy-ca-bundle ConfigMap โ€” do not re-extract from router/spoke API servers. +# After MirrorPeer (and vp-manage-proxy-cluster-ca populates vp-pattern-proxy-ca-bundle), copy CA from +# the hub vp-pattern-proxy-ca-bundle ConfigMap โ€” do not re-extract from router/spoke API servers. # Wait until Ramen hub config has s3StoreProfiles (from ODF/MirrorPeer), then patch caCertificates only. set -euo pipefail @@ -27,20 +27,23 @@ mkdir -p "$WORK_DIR" wait_for_trusted_ca() { local deadline=$((SECONDS + TRUSTED_CA_WAIT_SECONDS)) - echo "Waiting for cluster-proxy-ca-bundle (openshift-config) with non-trivial ca-bundle.crt (max ${TRUSTED_CA_WAIT_SECONDS}s)..." + # Use the differential bundle: cluster-specific CAs only (API + ingress, no system trust store). + # This is the correct material for Ramen s3StoreProfiles caCertificates โ€” concise and focused + # on the CAs needed to verify NooBaa S3 external route TLS certificates. + echo "Waiting for vp-pattern-proxy-ca-bundle-differential (openshift-config) with non-trivial cabundle (max ${TRUSTED_CA_WAIT_SECONDS}s)..." while ((SECONDS < deadline)); do local data bytes - data=$(oc get configmap cluster-proxy-ca-bundle -n openshift-config -o jsonpath='{.data.ca-bundle\.crt}' 2>/dev/null || true) + data=$(oc get configmap vp-pattern-proxy-ca-bundle-differential -n openshift-config -o jsonpath='{.data.cabundle}' 2>/dev/null || true) bytes=$(printf '%s' "$data" | wc -c | tr -d ' ') if [[ "${bytes:-0}" -ge 64 ]]; then printf '%s' "$data" >"$WORK_DIR/combined-ca-bundle.crt" - echo " โœ… trusted CA bundle captured (${bytes} bytes)" + echo " โœ… differential CA bundle captured (${bytes} bytes)" return 0 fi - echo " ... ca-bundle.crt bytes=${bytes:-0}, retry in ${POLL_INTERVAL}s" + echo " ... cabundle bytes=${bytes:-0}, retry in ${POLL_INTERVAL}s" sleep "$POLL_INTERVAL" done - die "cluster-proxy-ca-bundle not ready in time โ€” ensure ACM/ODF policy populated it (see opp-policy-chart policy-odf-managed-cluster-ssl)" + die "vp-pattern-proxy-ca-bundle-differential not ready in time โ€” ensure vp-manage-proxy-cluster-ca chart differentialBundle is enabled and synced" } count_s3_profiles() { diff --git a/ansible/roles/odf_ramen_trusted_ca/files/odf-ssl-ramen-hub-configmap.sh b/ansible/roles/odf_ramen_trusted_ca/files/odf-ssl-ramen-hub-configmap.sh index 6f81a5e..c94334e 100755 --- a/ansible/roles/odf_ramen_trusted_ca/files/odf-ssl-ramen-hub-configmap.sh +++ b/ansible/roles/odf_ramen_trusted_ca/files/odf-ssl-ramen-hub-configmap.sh @@ -112,20 +112,36 @@ if oc get configmap ramen-hub-operator-config -n openshift-operators &>/dev/null PATCHED_VIA_YQ=false if [[ -n "$EXISTING_YAML" ]]; then echo "$EXISTING_YAML" >"$WORK_DIR/existing-ramen-config.yaml" - if ! command -v yq &>/dev/null; then - die "yq is required (e.g. mikefarah/yq v4)" - fi - export CA_BUNDLE_BASE64 - YQ_PATCHED=false - if yq eval -i '.s3StoreProfiles[]? |= . + {"caCertificates": strenv(CA_BUNDLE_BASE64)}' "$WORK_DIR/existing-ramen-config.yaml" 2>/dev/null; then - YQ_PATCHED=true - fi - if yq eval -i '.kubeObjectProtection.s3StoreProfiles[]? |= . + {"caCertificates": strenv(CA_BUNDLE_BASE64)}' "$WORK_DIR/existing-ramen-config.yaml" 2>/dev/null; then - YQ_PATCHED=true - fi - if [[ "$YQ_PATCHED" != "true" ]]; then - die "yq could not patch s3StoreProfiles (check kubeObjectProtection / top-level s3StoreProfiles). yq: $(yq --version 2>/dev/null || true)" - fi + command -v python3 &>/dev/null || die "python3 not found โ€” cannot patch ramen_manager_config" + # Pure-Python patch using only built-in modules (no PyYAML required). + # Reads combined-ca-bundle.crt directly to avoid E2BIG env-var size limits. + # Strips any existing caCertificates lines then injects after each s3ProfileName. + WORK_DIR="$WORK_DIR" python3 - <<'PYEOF' +import os, re, base64 + +work_dir = os.environ["WORK_DIR"] + +with open(os.path.join(work_dir, "combined-ca-bundle.crt"), "rb") as f: + ca_b64 = base64.b64encode(f.read()).decode("ascii") + +path = os.path.join(work_dir, "existing-ramen-config.yaml") +with open(path, "r") as f: + lines = f.readlines() + +# Remove stale caCertificates lines so re-runs are idempotent. +lines = [l for l in lines if not re.match(r'^\s*caCertificates:', l)] + +result = [] +for line in lines: + result.append(line) + if re.match(r'^\s*s3ProfileName:', line): + indent = len(line) - len(line.lstrip()) + result.append(" " * indent + "caCertificates: " + ca_b64 + "\n") + +with open(path, "w") as f: + f.writelines(result) +PYEOF + [[ $? -eq 0 ]] || die "python3 failed to patch existing-ramen-config.yaml" grep -q "caCertificates" "$WORK_DIR/existing-ramen-config.yaml" || die "patched file has no caCertificates" cp "$WORK_DIR/existing-ramen-config.yaml" "$WORK_DIR/ramen_manager_config.yaml" PATCHED_VIA_YQ=true @@ -147,36 +163,32 @@ s3StoreProfiles: echo "$UPDATED_YAML" >"$WORK_DIR/ramen_manager_config.yaml" fi - echo " Building ConfigMap manifest (literal block) and oc apply..." - oc get configmap ramen-hub-operator-config -n openshift-operators -o yaml >"$WORK_DIR/ramen-configmap-template.yaml" 2>/dev/null || true + # Use oc patch --type=merge with a JSON patch file. + # oc apply is avoided: it stores the full manifest in last-applied-configuration, + # which exceeds the 262144-byte annotation limit when caCertificates is large. + echo " Patching ramen-hub-operator-config via oc patch --type=merge..." + PATCH_JSON="$WORK_DIR/ramen-patch.json" + WORK_DIR="$WORK_DIR" python3 - <<'PYEOF' +import json, os, sys +work_dir = os.environ["WORK_DIR"] +with open(os.path.join(work_dir, "ramen_manager_config.yaml"), "r") as f: + data = f.read() +with open(os.path.join(work_dir, "ramen-patch.json"), "w") as f: + json.dump({"data": {"ramen_manager_config.yaml": data}}, f) +PYEOF + [[ $? -eq 0 ]] || die "python3 failed to build ramen-patch.json" UPDATE_EXIT_CODE=1 UPDATE_OUTPUT="" - if [[ -f "$WORK_DIR/ramen-configmap-template.yaml" ]]; then - { - echo "apiVersion: v1" - echo "kind: ConfigMap" - echo "metadata:" - echo " name: ramen-hub-operator-config" - echo " namespace: openshift-operators" - echo "data:" - echo " ramen_manager_config.yaml: |" - sed 's/^/ /' "$WORK_DIR/ramen_manager_config.yaml" - } >"$WORK_DIR/ramen-configmap-updated.yaml" - if UPDATE_OUTPUT=$(oc apply -f "$WORK_DIR/ramen-configmap-updated.yaml" 2>&1); then - UPDATE_EXIT_CODE=0 - else - UPDATE_EXIT_CODE=$? - fi - rm -f "$WORK_DIR/ramen-configmap-template.yaml" "$WORK_DIR/ramen-configmap-updated.yaml" + if UPDATE_OUTPUT=$(oc patch configmap/ramen-hub-operator-config \ + -n openshift-operators \ + --type=merge \ + --patch-file="$PATCH_JSON" 2>&1); then + UPDATE_EXIT_CODE=0 else - if UPDATE_OUTPUT=$(oc set data configmap/ramen-hub-operator-config -n openshift-operators \ - ramen_manager_config.yaml="$(cat "$WORK_DIR/ramen_manager_config.yaml")" 2>&1); then - UPDATE_EXIT_CODE=0 - else - UPDATE_EXIT_CODE=$? - fi + UPDATE_EXIT_CODE=$? fi + rm -f "$PATCH_JSON" echo " Update exit code: $UPDATE_EXIT_CODE" echo " Update output: $UPDATE_OUTPUT" @@ -207,3 +219,27 @@ s3StoreProfiles: fi echo " ramen-hub-operator-config updated successfully with base64-encoded CA bundle in s3StoreProfiles" + +# Trigger Ramen to re-reconcile all VRGs so it updates ManifestWork objects on each +# managed cluster with the new caCertificates โ†’ BSL.spec.objectStorage.caCert. +echo "Triggering Ramen VRG reconciliation to propagate caCertificates to managed cluster BSLs..." +VRG_TRIGGER_FAILED=0 +while IFS= read -r line; do + NS=$(echo "$line" | awk '{print $1}') + NAME=$(echo "$line" | awk '{print $2}') + [[ -z "$NS" || -z "$NAME" ]] && continue + if oc annotate vrg "$NAME" -n "$NS" \ + ramendr.openshift.io/reconcile-trigger="$(date +%s)" \ + --overwrite 2>/dev/null; then + echo " โœ… Annotated VRG $NS/$NAME" + else + echo " โš ๏ธ Failed to annotate VRG $NS/$NAME" >&2 + VRG_TRIGGER_FAILED=1 + fi +done < <(oc get vrg -A --no-headers -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name' 2>/dev/null || true) + +if [[ $VRG_TRIGGER_FAILED -eq 0 ]]; then + echo " โœ… VRG reconciliation triggered โ€” Ramen will update spoke BSLs with caCertificates" +else + echo " โš ๏ธ Some VRG annotations failed; BSLs may need manual reconciliation" >&2 +fi diff --git a/templates/configmap-odf-ssl-playbooks.yaml b/templates/configmap-odf-ssl-playbooks.yaml index b2f3b1e..bbb58d2 100644 --- a/templates/configmap-odf-ssl-playbooks.yaml +++ b/templates/configmap-odf-ssl-playbooks.yaml @@ -1,3 +1,4 @@ +{{- if ((.Values.odfSslCertificateExtractor | default dict).enabled | default false) }} apiVersion: v1 kind: ConfigMap metadata: @@ -33,3 +34,4 @@ data: {{ .Files.Get "scripts/ansible/tasks/kubeconfig.yml" | indent 4 }} tasks_kubeconfig_attempt.yml: | {{ .Files.Get "scripts/ansible/tasks/kubeconfig-attempt.yml" | indent 4 }} +{{- end }} diff --git a/templates/job-odf-ssl-certificate-extraction.yaml b/templates/job-odf-ssl-certificate-extraction.yaml index 0217ffd..a109c22 100644 --- a/templates/job-odf-ssl-certificate-extraction.yaml +++ b/templates/job-odf-ssl-certificate-extraction.yaml @@ -1,3 +1,4 @@ +{{- if ((.Values.odfSslCertificateExtractor | default dict).enabled | default false) }} apiVersion: batch/v1 kind: Job metadata: @@ -71,3 +72,4 @@ spec: serviceAccountName: odf-ssl-extractor-sa backoffLimit: 10 activeDeadlineSeconds: 7200 +{{- end }} diff --git a/templates/job-odf-ssl-certificate-precheck.yaml b/templates/job-odf-ssl-certificate-precheck.yaml index a5cb657..bc8a1ba 100644 --- a/templates/job-odf-ssl-certificate-precheck.yaml +++ b/templates/job-odf-ssl-certificate-precheck.yaml @@ -1,3 +1,4 @@ +{{- if ((.Values.odfSslCertificateExtractor | default dict).enabled | default false) }} apiVersion: batch/v1 kind: Job metadata: @@ -62,3 +63,4 @@ spec: path: tasks/kubeconfig.yml - key: tasks_kubeconfig_attempt.yml path: tasks/kubeconfig-attempt.yml +{{- end }} diff --git a/templates/placement-binding-odf-ssl-hub.yaml b/templates/placement-binding-odf-ssl-hub.yaml index 16ce7f1..8c6949c 100644 --- a/templates/placement-binding-odf-ssl-hub.yaml +++ b/templates/placement-binding-odf-ssl-hub.yaml @@ -1,3 +1,4 @@ +{{- if ((.Values.odfSslCertificateExtractor | default dict).enabled | default false) }} apiVersion: policy.open-cluster-management.io/v1 kind: PlacementBinding metadata: @@ -17,3 +18,4 @@ subjects: - name: policy-odf-ssl-certificate-management kind: Policy apiGroup: policy.open-cluster-management.io +{{- end }} diff --git a/templates/placement-binding-odf-ssl.yaml b/templates/placement-binding-odf-ssl.yaml index 47132c9..613ca36 100644 --- a/templates/placement-binding-odf-ssl.yaml +++ b/templates/placement-binding-odf-ssl.yaml @@ -1,3 +1,4 @@ +{{- if ((.Values.odfSslCertificateExtractor | default dict).enabled | default false) }} apiVersion: policy.open-cluster-management.io/v1 kind: PlacementBinding metadata: @@ -17,3 +18,4 @@ subjects: - name: policy-odf-managed-cluster-ssl kind: Policy apiGroup: policy.open-cluster-management.io +{{- end }} diff --git a/templates/placement-odf-ssl-certificates.yaml b/templates/placement-odf-ssl-certificates.yaml index dc9a2db..d7ce4cc 100644 --- a/templates/placement-odf-ssl-certificates.yaml +++ b/templates/placement-odf-ssl-certificates.yaml @@ -1,3 +1,4 @@ +{{- if ((.Values.odfSslCertificateExtractor | default dict).enabled | default false) }} apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: @@ -26,3 +27,4 @@ spec: - key: name operator: NotIn values: ["local-cluster"] +{{- end }} diff --git a/templates/placement-odf-ssl-hub.yaml b/templates/placement-odf-ssl-hub.yaml index 708d30d..c7dbda7 100644 --- a/templates/placement-odf-ssl-hub.yaml +++ b/templates/placement-odf-ssl-hub.yaml @@ -1,3 +1,4 @@ +{{- if ((.Values.odfSslCertificateExtractor | default dict).enabled | default false) }} apiVersion: apps.open-cluster-management.io/v1 kind: PlacementRule metadata: @@ -19,3 +20,4 @@ spec: operator: In values: - local-cluster +{{- end }} diff --git a/templates/policy-odf-ssl-certificate-management.yaml b/templates/policy-odf-ssl-certificate-management.yaml index 19838af..7bb59dd 100644 --- a/templates/policy-odf-ssl-certificate-management.yaml +++ b/templates/policy-odf-ssl-certificate-management.yaml @@ -17,7 +17,7 @@ metadata: name: policy-odf-ssl-certificate-management namespace: open-cluster-management spec: - disabled: false + disabled: true policy-templates: - objectDefinition: apiVersion: policy.open-cluster-management.io/v1 diff --git a/templates/rbac-odf-ssl-certificate-precheck.yaml b/templates/rbac-odf-ssl-certificate-precheck.yaml index 50617cf..fd283a8 100644 --- a/templates/rbac-odf-ssl-certificate-precheck.yaml +++ b/templates/rbac-odf-ssl-certificate-precheck.yaml @@ -1,3 +1,4 @@ +{{- if ((.Values.odfSslCertificateExtractor | default dict).enabled | default false) }} apiVersion: v1 kind: ServiceAccount metadata: @@ -58,4 +59,5 @@ roleRef: subjects: - kind: ServiceAccount name: odf-ssl-certificate-precheck - namespace: {{ include "opp.clusterCaMgtNamespace" . }} \ No newline at end of file + namespace: {{ include "opp.clusterCaMgtNamespace" . }} +{{- end }} \ No newline at end of file diff --git a/templates/rbac-odf-ssl-extractor.yaml b/templates/rbac-odf-ssl-extractor.yaml index e6df99b..36042ad 100644 --- a/templates/rbac-odf-ssl-extractor.yaml +++ b/templates/rbac-odf-ssl-extractor.yaml @@ -1,3 +1,4 @@ +{{- if ((.Values.odfSslCertificateExtractor | default dict).enabled | default false) }} apiVersion: v1 kind: ServiceAccount metadata: @@ -59,3 +60,4 @@ roleRef: kind: ClusterRole name: odf-ssl-extractor-role apiGroup: rbac.authorization.k8s.io +{{- end }} diff --git a/values.yaml b/values.yaml index 604cecf..0197dc5 100644 --- a/values.yaml +++ b/values.yaml @@ -51,17 +51,20 @@ ansible: # -- Argo CD resource sync-options applied to the Ansible ConfigMap. configMapArgoSyncOptions: Prune=false,ServerSideApply=true -# Post-sync job odf-ramen-trusted-ca (wave 9): CA from hub cluster-proxy-ca-bundle; waits for Ramen s3StoreProfiles. +# Post-sync job odf-ramen-trusted-ca (wave 9): CA from hub vp-pattern-proxy-ca-bundle; waits for Ramen s3StoreProfiles. odfRamenTrustedCa: # -- Maximum seconds to wait for Ramen s3StoreProfiles to be populated before the trusted-CA job gives up. ramenS3WaitSeconds: 3600 - # -- Maximum seconds to wait for the hub cluster-proxy-ca-bundle trusted CA before the job gives up. + # -- Maximum seconds to wait for the hub vp-pattern-proxy-ca-bundle trusted CA before the job gives up. trustedCaWaitSeconds: 3600 # -- Polling interval in seconds for readiness checks inside the trusted-CA job. pollInterval: 15 # odf-ssl-certificate-extractor Job: wait for both DR ManagedClusters before extraction. +# Set enabled: false when vp-manage-proxy-cluster-ca-chart handles CA collection. odfSslCertificateExtractor: + # -- When false, skip legacy SSL certificate extraction jobs (disable when using vp-manage-proxy-cluster-ca). + enabled: false # -- Maximum number of attempts to check DR ManagedCluster readiness before the extractor job fails. clusterReadinessMaxAttempts: 150 # -- Seconds to sleep between each ManagedCluster readiness poll attempt. From 89718cedab224efb511275eec263f92319b9a647 Mon Sep 17 00:00:00 2001 From: OAharoni-RedHat Date: Tue, 21 Jul 2026 13:24:24 -0400 Subject: [PATCH 2/5] test logging --- .../files/odf-ramen-trusted-ca.sh | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/ansible/roles/odf_ramen_trusted_ca/files/odf-ramen-trusted-ca.sh b/ansible/roles/odf_ramen_trusted_ca/files/odf-ramen-trusted-ca.sh index 73be1d3..a331320 100755 --- a/ansible/roles/odf_ramen_trusted_ca/files/odf-ramen-trusted-ca.sh +++ b/ansible/roles/odf_ramen_trusted_ca/files/odf-ramen-trusted-ca.sh @@ -14,6 +14,12 @@ POLL_INTERVAL="${POLL_INTERVAL:-15}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" RAMEN_SCRIPT="${SCRIPT_DIR}/odf-ssl-ramen-hub-configmap.sh" +LOG_FILE="${WORK_DIR:-/tmp/odf-ssl-certs}/ramen-trusted-ca.log" +mkdir -p "${WORK_DIR:-/tmp/odf-ssl-certs}" +# Tee all output to a log file readable via oc exec while Ansible buffers stdout. +exec > >(tee -a "$LOG_FILE") 2>&1 +echo "[$(date -u +%T)] odf-ramen-trusted-ca.sh started" + die() { echo "โŒ odf-ramen-trusted-ca.sh: $*" >&2 exit 1 @@ -69,14 +75,18 @@ wait_for_ramen_s3_profiles() { echo "Waiting for ramen-hub-operator-config s3StoreProfiles (openshift-operators, max ${RAMEN_CM_WAIT_SECONDS}s)..." while ((SECONDS < deadline)); do yaml=$(oc get configmap ramen-hub-operator-config -n openshift-operators -o jsonpath='{.data.ramen_manager_config\.yaml}' 2>/dev/null || true) - if [[ -n "$yaml" ]] && echo "$yaml" | grep -q 's3StoreProfiles'; then - c=$(count_s3_profiles "$yaml") + local yaml_empty="YES" grep_match="NO" + [[ -n "$yaml" ]] && yaml_empty="NO" + echo "$yaml" | grep -q 's3StoreProfiles' 2>/dev/null && grep_match="YES" || true + c=$(count_s3_profiles "$yaml") + echo " [$(date -u +%T)] yaml_empty=${yaml_empty} grep_match=${grep_match} count=${c}" + if [[ "$yaml_empty" == "NO" && "$grep_match" == "YES" ]]; then if [[ "${c:-0}" -ge 2 ]]; then echo " โœ… ramen_manager_config has s3StoreProfiles (countโ‰ˆ$c)" return 0 fi fi - echo " ... profiles not ready yet (need >=2), retry in ${POLL_INTERVAL}s" + echo " ... profiles not ready yet (need >=2, got ${c:-0}), retry in ${POLL_INTERVAL}s" sleep "$POLL_INTERVAL" done die "ramen-hub-operator-config never gained s3StoreProfiles โ€” confirm MirrorPeer and hub Ramen operator reconciled" From b4ec696cbca7c2daa54144d23fcb0796a68731ef Mon Sep 17 00:00:00 2001 From: OAharoni-RedHat Date: Tue, 21 Jul 2026 13:31:16 -0400 Subject: [PATCH 3/5] fix counting --- .../files/odf-ramen-trusted-ca.sh | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/ansible/roles/odf_ramen_trusted_ca/files/odf-ramen-trusted-ca.sh b/ansible/roles/odf_ramen_trusted_ca/files/odf-ramen-trusted-ca.sh index a331320..c079112 100755 --- a/ansible/roles/odf_ramen_trusted_ca/files/odf-ramen-trusted-ca.sh +++ b/ansible/roles/odf_ramen_trusted_ca/files/odf-ramen-trusted-ca.sh @@ -75,16 +75,13 @@ wait_for_ramen_s3_profiles() { echo "Waiting for ramen-hub-operator-config s3StoreProfiles (openshift-operators, max ${RAMEN_CM_WAIT_SECONDS}s)..." while ((SECONDS < deadline)); do yaml=$(oc get configmap ramen-hub-operator-config -n openshift-operators -o jsonpath='{.data.ramen_manager_config\.yaml}' 2>/dev/null || true) - local yaml_empty="YES" grep_match="NO" - [[ -n "$yaml" ]] && yaml_empty="NO" - echo "$yaml" | grep -q 's3StoreProfiles' 2>/dev/null && grep_match="YES" || true + # Strip carriage returns โ€” configmap YAML may have CRLF line endings. + yaml=$(printf '%s' "$yaml" | tr -d '\r') c=$(count_s3_profiles "$yaml") - echo " [$(date -u +%T)] yaml_empty=${yaml_empty} grep_match=${grep_match} count=${c}" - if [[ "$yaml_empty" == "NO" && "$grep_match" == "YES" ]]; then - if [[ "${c:-0}" -ge 2 ]]; then - echo " โœ… ramen_manager_config has s3StoreProfiles (countโ‰ˆ$c)" - return 0 - fi + echo " [$(date -u +%T)] yaml_empty=$([ -z "$yaml" ] && echo YES || echo NO) count=${c}" + if [[ -n "$yaml" && "${c:-0}" -ge 2 ]]; then + echo " โœ… ramen_manager_config has s3StoreProfiles (countโ‰ˆ$c)" + return 0 fi echo " ... profiles not ready yet (need >=2, got ${c:-0}), retry in ${POLL_INTERVAL}s" sleep "$POLL_INTERVAL" From f22d1a0ed1a7e3198602e8f6fbb1b92950afda00 Mon Sep 17 00:00:00 2001 From: OAharoni-RedHat Date: Wed, 22 Jul 2026 09:53:10 -0400 Subject: [PATCH 4/5] fix linter and helm docs --- README.md | 3 ++- .../files/odf-ssl-ramen-hub-configmap.sh | 6 ++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b747555..1e521fc 100644 --- a/README.md +++ b/README.md @@ -27,9 +27,10 @@ v0.1.0 - Initial release | odf.postInstallFixesEnabled | bool | `true` | Enable ODF post-install automation (MirrorPeer, prerequisites checker, Ramen trusted-CA jobs/RBAC). | | odfRamenTrustedCa.pollInterval | int | `15` | Polling interval in seconds for readiness checks inside the trusted-CA job. | | odfRamenTrustedCa.ramenS3WaitSeconds | int | `3600` | Maximum seconds to wait for Ramen s3StoreProfiles to be populated before the trusted-CA job gives up. | -| odfRamenTrustedCa.trustedCaWaitSeconds | int | `3600` | Maximum seconds to wait for the hub cluster-proxy-ca-bundle trusted CA before the job gives up. | +| odfRamenTrustedCa.trustedCaWaitSeconds | int | `3600` | Maximum seconds to wait for the hub vp-pattern-proxy-ca-bundle trusted CA before the job gives up. | | odfSslCertificateExtractor.clusterReadinessMaxAttempts | int | `150` | Maximum number of attempts to check DR ManagedCluster readiness before the extractor job fails. | | odfSslCertificateExtractor.clusterReadinessSleepSeconds | int | `30` | Seconds to sleep between each ManagedCluster readiness poll attempt. | +| odfSslCertificateExtractor.enabled | bool | `false` | When false, skip legacy SSL certificate extraction jobs (disable when using vp-manage-proxy-cluster-ca). | | regionalDR[0].globalnetEnabled | bool | `false` | Enable Submariner Globalnet. Required when primary and secondary cluster CIDRs overlap. | | regionalDR[0].name | string | `"resilient"` | Name of this DR pair set. Must be unique within the regionalDR list and match the ACM policy placement label. | | submariner.NATTEnable | bool | `true` | Enable NAT traversal (NAT-T) for Submariner IPsec tunnels. | diff --git a/ansible/roles/odf_ramen_trusted_ca/files/odf-ssl-ramen-hub-configmap.sh b/ansible/roles/odf_ramen_trusted_ca/files/odf-ssl-ramen-hub-configmap.sh index c94334e..a5e2943 100755 --- a/ansible/roles/odf_ramen_trusted_ca/files/odf-ssl-ramen-hub-configmap.sh +++ b/ansible/roles/odf_ramen_trusted_ca/files/odf-ssl-ramen-hub-configmap.sh @@ -116,7 +116,7 @@ if oc get configmap ramen-hub-operator-config -n openshift-operators &>/dev/null # Pure-Python patch using only built-in modules (no PyYAML required). # Reads combined-ca-bundle.crt directly to avoid E2BIG env-var size limits. # Strips any existing caCertificates lines then injects after each s3ProfileName. - WORK_DIR="$WORK_DIR" python3 - <<'PYEOF' + WORK_DIR="$WORK_DIR" python3 - <<'PYEOF' || die "python3 failed to patch existing-ramen-config.yaml" import os, re, base64 work_dir = os.environ["WORK_DIR"] @@ -141,7 +141,6 @@ for line in lines: with open(path, "w") as f: f.writelines(result) PYEOF - [[ $? -eq 0 ]] || die "python3 failed to patch existing-ramen-config.yaml" grep -q "caCertificates" "$WORK_DIR/existing-ramen-config.yaml" || die "patched file has no caCertificates" cp "$WORK_DIR/existing-ramen-config.yaml" "$WORK_DIR/ramen_manager_config.yaml" PATCHED_VIA_YQ=true @@ -168,7 +167,7 @@ s3StoreProfiles: # which exceeds the 262144-byte annotation limit when caCertificates is large. echo " Patching ramen-hub-operator-config via oc patch --type=merge..." PATCH_JSON="$WORK_DIR/ramen-patch.json" - WORK_DIR="$WORK_DIR" python3 - <<'PYEOF' + WORK_DIR="$WORK_DIR" python3 - <<'PYEOF' || die "python3 failed to build ramen-patch.json" import json, os, sys work_dir = os.environ["WORK_DIR"] with open(os.path.join(work_dir, "ramen_manager_config.yaml"), "r") as f: @@ -176,7 +175,6 @@ with open(os.path.join(work_dir, "ramen_manager_config.yaml"), "r") as f: with open(os.path.join(work_dir, "ramen-patch.json"), "w") as f: json.dump({"data": {"ramen_manager_config.yaml": data}}, f) PYEOF - [[ $? -eq 0 ]] || die "python3 failed to build ramen-patch.json" UPDATE_EXIT_CODE=1 UPDATE_OUTPUT="" From 0a877e1fc2a01605db0e83c6b41e37777c5e3671 Mon Sep 17 00:00:00 2001 From: OAharoni-RedHat Date: Wed, 22 Jul 2026 10:01:24 -0400 Subject: [PATCH 5/5] upgrade version --- Chart.yaml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Chart.yaml b/Chart.yaml index f4c535d..021fcd1 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -8,4 +8,4 @@ keywords: - odf - pattern - regionaldr -version: 0.0.1 +version: 0.0.2 diff --git a/README.md b/README.md index 1e521fc..82789d8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # odf-dr-chart -![Version: 0.0.1](https://img.shields.io/badge/Version-0.0.1-informational?style=flat-square) +![Version: 0.0.2](https://img.shields.io/badge/Version-0.0.2-informational?style=flat-square) Standalone Helm chart for ODF storage infrastructure supporting Regional DR. Deploys ODF SSL certificate extraction, Submariner network overlay, MirrorPeer storage mirroring, ODF DR prerequisites.