Skip to content

Conversation

chiragkyal
Copy link
Member

@chiragkyal chiragkyal commented Sep 11, 2025

Summary

This PR adds support for user-configurable Istio CA certificates by utilising the istioCACertificate field. Users can now reference a ConfigMap containing their CA certificate.

Description

  • Users can now specify a ConfigMap reference in the IstioCSR resource to provide their own CA certificate
  • The CA certificate ConfigMap can be in the same namespace as the IstioCSR resource or in a different namespace (it will fallback to IstioCSR namespace, if no namespace is provided)
  • The operator validates that the referenced certificate is a valid CA certificate.
    • Validates the existence and content of the ConfigMap.
  • The operator watches the source ConfigMap and updates the mounted certificate when it changes.
  • Creates a managed copy of the ConfigMap (cert-manager-istio-csr-issuer-ca-copy) in the IstioCSR namespace
    • If the operator-managed ConfigMap copy is manually modified, it gets reconciled back to the desired state.
  • Mounts the managed copy in the istio-csr deployment at /var/run/configmaps/istio-csr
  • When istioCACertificate is not provided, the operator instead of looking in the configured Issuer/ClusterIssuer, will instead look in the secret containing the certificates obtained from cert-manager for istiod server.

EP: openshift/enhancements#1837

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Sep 11, 2025
@openshift-ci openshift-ci bot requested a review from bharath-b-rh September 11, 2025 05:25
@openshift-ci-robot
Copy link

openshift-ci-robot commented Sep 11, 2025

@chiragkyal: This pull request references CM-679 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target either version "4.21." or "openshift-4.21.", but it targets "cert-manager-1.18" instead.

In response to this:

/cc @bharath-b-rh

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link

coderabbitai bot commented Sep 11, 2025

Walkthrough

Adds opt-in user-provided CA ConfigMap handling and a separate issuer-based CA flow, PEM validation, a generic resource watch-label updater, exported constants and ConfigMap watch predicates, refactored CA ConfigMap creation/mounting, and accompanying unit and e2e tests and templates.

Changes

Cohort / File(s) Summary
Controller logic: CA handling & watches
pkg/controller/istiocsr/deployments.go, pkg/controller/istiocsr/controller.go, pkg/controller/istiocsr/constants.go
Adds user-provided CA flow (handleUserProvidedCA), issuer-based CA flow (handleIssuerBasedCA), PEM validation (validatePEMData), consolidated CA ConfigMap create/update helpers (createOrUpdateCAConfigMap, createCAConfigMapFromIssuerSecret, createCaConfigMapFromIstiodCertificate), generic resource watch-label updater (updateWatchLabel(obj, ...)), renames/exports constants (Istiocsr*), adds ConfigMap watch predicates, and refactors deployment volume/mount and arg handling.
Unit tests & test utilities
pkg/controller/istiocsr/deployments_test.go, pkg/controller/istiocsr/test_utils.go
Expands unit tests for many CA ConfigMap scenarios (valid, missing, invalid PEM, wrong usages, watch-label failures), adds TestUpdateVolumeWithIssuerCA, introduces certificate generation helpers (GenerateCertificate, CertificateTweak), and test ConfigMap/Secret helpers using IstiocsrCAKeyName.
Controller tests & install
pkg/controller/istiocsr/controller_test.go, pkg/controller/istiocsr/install_instiocsr_test.go
Extends test mocks to include Issuer, Secret, and ConfigMap pre-population and Get/Exists behavior; updates reconciliation tests to exercise CA/issuer/secret paths.
End-to-end tests & templates
test/e2e/istio_csr_test.go, test/e2e/testdata/istio/istiocsr_with_ca_certificate_template.yaml
Adds E2E scenarios for CA ConfigMap propagation/copying, watch-label verification, mounting validation across namespaces, reconciliation on changes, negative cases, and converts some cleanup to DeferCleanup; adds templated IstioCSR manifest referencing a CA ConfigMap.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.25% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "CM-679: Implements user-configurable CA certificate support for Istio CSR" directly summarizes the main objective of the changeset. It clearly communicates the primary feature being added—support for user-configurable CA certificates in Istio CSR. The changes across deployments.go (CA handling flows, ConfigMap management, PEM validation), constants.go (constant exports), controller.go (ConfigMap watches), and extensive test coverage all align with this stated purpose. The title is concise, specific, and avoids vague terminology or noise.
Description Check ✅ Passed The PR description is clearly related to the changeset. It explains the feature being implemented, including that users can reference a ConfigMap for CA certificates, that the operator validates the certificate, watches for changes, creates a managed copy in the IstioCSR namespace, mounts it at a specific path, and falls back to cert-manager secrets when istioCACertificate is not provided. These points are substantiated by the code changes: CA validation logic (validatePEMData), watch-label functionality, ConfigMap creation and management (createOrUpdateCAConfigMap), volume mounting logic, and controller-level ConfigMap watching. The description is sufficiently detailed without being overly verbose.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.5.0)

Error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions
The command is terminated due to an error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (9)
pkg/controller/istiocsr/test_utils.go (1)

312-322: Helper OK

createCertificateConfigMap aligns with test expectations (key "ca-cert.pem"). Consider parameterizing the key to reuse for negative-key tests.

pkg/controller/istiocsr/deployments_test.go (2)

450-451: Error message: missing resource name and double space

The expected string shows “failed to update resource with watch label…”. Likely the reconciler omitted object identification, leading to unclear logs and brittle tests. Include kind/name in the message (as done in Line 921 scenario) and update this expectation accordingly.


781-809: Negative CA ConfigMap coverage is comprehensive

Great assertions for not found, missing key, invalid PEM, watch-label update failure, non-CA, and missing CertSign. Consider adding one assertion to inspect the generated Deployment for the presence/absence of the CA volume/mount to strengthen behavioral validation.

Also applies to: 811-842, 844-875, 877-922, 924-954, 956-986

config/crd/bases/operator.openshift.io_istiocsrs.yaml (2)

1031-1050: Consider making the IstioCACertificate field immutable after creation.

The istioCACertificate field allows users to configure a CA certificate from a ConfigMap. Once set, changing this field could lead to certificate validation issues and potential service disruptions. Consider adding an immutability validation rule similar to other critical fields in this CRD.

Add an x-kubernetes-validations rule to prevent changes after creation:

                      istioCACertificate:
                        description: |-
                          istioCACertificate when provided, the operator will use the CA certificate from the specified ConfigMap. If empty, the operator will
                          automatically extract the CA certificate from the Secret containing the istiod certificate obtained from cert-manager.
                        properties:
                          key:
                            description: key name holding the required data.
                            type: string
                          name:
                            description: name of the ConfigMap.
                            type: string
                          namespace:
                            description: namespace in which the ConfigMap exists.
                              If empty, ConfigMap will be looked up in the IstioCSR
                              namespace.
                            type: string
                        required:
                        - key
                        - name
                        type: object
+                       x-kubernetes-validations:
+                       - message: istioCACertificate is immutable once set
+                         rule: self == oldSelf

1033-1034: Improve field description clarity.

The description could be clearer about the behavior when this field is not provided.

-                        description: |-
-                          istioCACertificate when provided, the operator will use the CA certificate from the specified ConfigMap. If empty, the operator will
-                          automatically extract the CA certificate from the Secret containing the istiod certificate obtained from cert-manager.
+                        description: |-
+                          istioCACertificate references a ConfigMap containing the CA certificate to be used by istio-csr.
+                          When provided, the operator will use the CA certificate from the specified ConfigMap.
+                          When not provided, the operator will automatically extract the CA certificate from the issuer's Secret.
pkg/controller/istiocsr/deployments.go (4)

267-282: Consider improving error messages for better troubleshooting.

The error wrapping could be more specific about which CA provisioning path failed.

 func (r *Reconciler) updateVolumes(deployment *appsv1.Deployment, istiocsr *v1alpha1.IstioCSR, resourceLabels map[string]string) error {
 	// Use user-configured CA certificate if provided
 	if istiocsr.Spec.IstioCSRConfig.CertManager.IstioCACertificate != nil {
 		if err := r.handleUserProvidedCA(deployment, istiocsr, resourceLabels); err != nil {
-			return FromError(err, "failed to validate and mount CA certificate ConfigMap")
+			return FromError(err, "failed to handle user-provided CA certificate from ConfigMap")
 		}
 		return nil
 	}
 
 	// Fall back to issuer-based CA certificate if CA certificate is not configured
 	// Handle issuer-based CA configuration
 	if err := r.handleIssuerBasedCA(deployment, istiocsr, resourceLabels); err != nil {
-		return err
+		return FromError(err, "failed to handle issuer-based CA certificate")
 	}
 
 	return nil
 }

284-291: Remove unnecessary nil check.

The nil check for caCertConfig is redundant since this function is only called when IstioCACertificate is not nil.

 func (r *Reconciler) handleUserProvidedCA(deployment *appsv1.Deployment, istiocsr *v1alpha1.IstioCSR, resourceLabels map[string]string) error {
 	caCertConfig := istiocsr.Spec.IstioCSRConfig.CertManager.IstioCACertificate
-	if caCertConfig == nil {
-		return NewIrrecoverableError(fmt.Errorf("CA certificate configuration is nil"), "CA certificate validation failed")
-	}
 
 	// Determine the namespace - use specified namespace or default to IstioCSR namespace

529-542: Consider optimizing label updates to avoid unnecessary API calls.

The function always calls UpdateWithRetry even if the label already exists with the correct value.

 func (r *Reconciler) updateWatchLabel(obj client.Object, istiocsr *v1alpha1.IstioCSR) error {
 	labels := obj.GetLabels()
 	if labels == nil {
 		labels = make(map[string]string)
 	}
+	expectedValue := fmt.Sprintf(istiocsrResourceWatchLabelValueFmt, istiocsr.GetNamespace(), istiocsr.GetName())
+	if labels[istiocsrResourceWatchLabelName] == expectedValue {
+		// Label already set correctly, no update needed
+		return nil
+	}
-	labels[istiocsrResourceWatchLabelName] = fmt.Sprintf(istiocsrResourceWatchLabelValueFmt, istiocsr.GetNamespace(), istiocsr.GetName())
+	labels[istiocsrResourceWatchLabelName] = expectedValue
 	obj.SetLabels(labels)
 
 	if err := r.UpdateWithRetry(r.ctx, obj); err != nil {
 		return fmt.Errorf("failed to update %s resource with watch label: %w", obj.GetName(), err)
 	}
 	return nil
 }

307-310: Enhance error message to include available keys.

When the specified key is not found in the ConfigMap, it would be helpful to list the available keys to aid debugging.

 	// Validate that the specified key exists in the ConfigMap
 	if _, exists := sourceConfigMap.Data[caCertConfig.Key]; !exists {
-		return NewIrrecoverableError(fmt.Errorf("key %q not found in ConfigMap %s/%s", caCertConfig.Key, sourceConfigMapKey.Namespace, sourceConfigMapKey.Name), "CA certificate ConfigMap %s/%s does not contain required key %q", sourceConfigMapKey.Namespace, sourceConfigMapKey.Name, caCertConfig.Key)
+		availableKeys := make([]string, 0, len(sourceConfigMap.Data))
+		for k := range sourceConfigMap.Data {
+			availableKeys = append(availableKeys, k)
+		}
+		return NewIrrecoverableError(fmt.Errorf("key %q not found in ConfigMap %s/%s, available keys: %v", caCertConfig.Key, sourceConfigMapKey.Namespace, sourceConfigMapKey.Name, availableKeys), "CA certificate ConfigMap %s/%s does not contain required key %q, available keys: %v", sourceConfigMapKey.Namespace, sourceConfigMapKey.Name, caCertConfig.Key, availableKeys)
 	}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge Base: Disabled due to Reviews > Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between baafb49 and dff9e11.

📒 Files selected for processing (9)
  • api/operator/v1alpha1/istiocsr_types.go (1 hunks)
  • api/operator/v1alpha1/zz_generated.deepcopy.go (3 hunks)
  • config/crd/bases/operator.openshift.io_istiocsrs.yaml (1 hunks)
  • pkg/controller/istiocsr/deployments.go (7 hunks)
  • pkg/controller/istiocsr/deployments_test.go (2 hunks)
  • pkg/controller/istiocsr/test_utils.go (3 hunks)
  • pkg/operator/applyconfigurations/operator/v1alpha1/certmanagerconfig.go (2 hunks)
  • pkg/operator/applyconfigurations/operator/v1alpha1/configmapreference.go (1 hunks)
  • pkg/operator/applyconfigurations/utils.go (1 hunks)
🔇 Additional comments (11)
api/operator/v1alpha1/istiocsr_types.go (1)

149-155: Verify IstioCACertificate live-update behavior

  • CRD: istioCACertificate is present in config/crd/bases/operator.openshift.io_istiocsrs.yaml (around lines 1031–1035).
  • Controller: field is referenced in pkg/controller/istiocsr/deployments.go (updateVolumes, handleUserProvidedCA) and exercised in pkg/controller/istiocsr/deployments_test.go.
  • Action required: confirm whether live updates are intended. If yes, confirm the controller implementation performs all of the following: re-labels/watches the source ConfigMap, atomically copies/updates the mounted ConfigMap (no stale data left on volumes), and triggers a Deployment rollout that doesn't leave stale volumes mounted. If live updates are not intended, make the field create-time only via XValidation (like issuerRef).
  • Meta: CI bot flagged Jira target version mismatch for master — set the issue target to 4.21.* to unblock automation.
pkg/operator/applyconfigurations/utils.go (1)

33-35: Mapping for ConfigMapReference apply-configuration looks correct

Case entry matches the new nested type; no issues spotted.

pkg/controller/istiocsr/test_utils.go (1)

42-42: Nice: tweakable certificate template for tests

The functional option keeps test cases concise.

api/operator/v1alpha1/zz_generated.deepcopy.go (3)

60-64: Deep-copy for IstioCACertificate pointer added correctly

Shallow copy is fine since fields are value types. Regenerated file—no manual edits.


236-250: ConfigMapReference DeepCopy methods present

Generation looks consistent; nothing further.


337-343: IstioCSRConfig now deep-copies CertManager

Prevents aliasing; good catch.

pkg/controller/istiocsr/deployments_test.go (1)

712-746: Positive CA ConfigMap scenarios are well covered

Happy path tests for same-namespace and custom-namespace CMs look good and exercise label update + mount flows.

Also applies to: 748-779

pkg/operator/applyconfigurations/operator/v1alpha1/certmanagerconfig.go (1)

1-37: LGTM! Well-structured apply configuration.

The implementation correctly adds support for the IstioCACertificate field with proper builder pattern methods. The generated code follows the established conventions for apply configurations.

pkg/operator/applyconfigurations/operator/v1alpha1/configmapreference.go (1)

1-42: LGTM! Clean builder implementation for ConfigMapReference.

The generated apply configuration correctly implements the builder pattern for the ConfigMapReference type with all required fields (Name, Namespace, Key).

pkg/controller/istiocsr/deployments.go (2)

493-527: Comprehensive PEM and CA certificate validation.

Excellent implementation of PEM validation with thorough checks for CA certificate requirements including BasicConstraints and KeyUsage verification.


513-515: Keep IsCA validation — the signing certificate must be a CA.
Istio supports root+intermediate chains, but the certificate that actually signs workload certs (root or intermediate signer) must have CA:TRUE, so rejecting non-CA certificates here is correct.

Likely an incorrect or invalid review comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
pkg/controller/istiocsr/test_utils.go (2)

5-9: Missing import for serial number generation (math/big)

Required for setting x509.Certificate.SerialNumber.

 import (
   "context"
   "crypto/rand"
   "crypto/rsa"
   "crypto/x509"
   "crypto/x509/pkix"
   "encoding/pem"
+  "math/big"
   "fmt"
   "testing"
   "time"
 )

274-310: Set a non-nil, random SerialNumber before CreateCertificate

Without SerialNumber, x509.CreateCertificate errors and tests panic.

 func GenerateCertificate(commonName string, organization []string, tweak CertificateTweak) string {
   // Generate RSA private key
   privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
   if err != nil {
     panic(fmt.Sprintf("Failed to generate private key: %v", err))
   }

   template := &x509.Certificate{
     Subject: pkix.Name{
       CommonName:   commonName,
       Organization: organization,
     },
     NotBefore:             time.Now(),
     NotAfter:              time.Now().Add(24 * time.Hour),
     KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
     ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
     BasicConstraintsValid: true,
     IsCA:                  false,
   }

   // Apply tweaks to modify the template
   tweak(template)

+  // Assign a random serial number
+  serialLimit := new(big.Int).Lsh(big.NewInt(1), 128)
+  serial, err := rand.Int(rand.Reader, serialLimit)
+  if err != nil {
+    panic(fmt.Sprintf("Failed to generate serial number: %v", err))
+  }
+  template.SerialNumber = serial
+
   certDER, err := x509.CreateCertificate(rand.Reader, template, template, &privateKey.PublicKey, privateKey)
   if err != nil {
     panic(fmt.Sprintf("Failed to create certificate: %v", err))
   }
🧹 Nitpick comments (5)
pkg/controller/istiocsr/constants.go (1)

86-96: Polish exported doc comments (capitalize ConfigMap, tighten phrasing)

Minor comment nits for consistency with Go doc and k8s terminology.

-// IstioCSRCAConfigMapName is the exported name of the configmap which is mounted in istiocsr container, containing the
+// IstioCSRCAConfigMapName is the exported name of the ConfigMap mounted in the istiocsr container, containing the
 // CA certificate configured in the secret referenced in the issuer.
 IstioCSRCAConfigMapName = istiocsrCAConfigMapName

-// IstioCSRCAKeyName is the exported key name holding the CA certificate in the issuer secret or the controller
-// CA configmap.
+// IstioCSRCAKeyName is the exported key name holding the CA certificate in the issuer secret or the controller
+// CA ConfigMap.
 IstioCSRCAKeyName = istiocsrCAKeyName

-// IstioCSRResourceWatchLabelName is the exported label name for identifying the resources of interest for the
-// controller but does not create or manage the resource.
+// IstioCSRResourceWatchLabelName is the exported label name for identifying resources of interest for the
+// controller that it does not create or manage.
 IstioCSRResourceWatchLabelName = istiocsrResourceWatchLabelName
pkg/controller/istiocsr/test_utils.go (1)

243-252: Include CRLSign for CA certificates (test robustness)

For CA examples, add CRLSign alongside CertSign to better emulate typical CA usage.

 func testCACertificateConfigMap() *corev1.ConfigMap {
   caPEM := GenerateCertificate("Test CA", []string{"cert-manager-operator"},
     func(cert *x509.Certificate) {
       cert.IsCA = true
-      cert.KeyUsage |= x509.KeyUsageCertSign
+      cert.KeyUsage |= x509.KeyUsageCertSign | x509.KeyUsageCRLSign
     },
   )
   return createCertificateConfigMap("ca-cert-test", testIstioCSRNamespace, caPEM)
 }
test/e2e/istio_csr_test.go (3)

413-433: Drop commented debug block

Dead commented code adds noise; rely on existing wait helpers or GinkgoWriter if needed.

-			// Add some debugging before calling waitForIstioCSRReady
-			// Eventually(func() error {
-			// 	istiocsrClient := loader.DynamicClient.Resource(istiocsrSchema).Namespace(ns.Name)
-			// 	customResource, err := istiocsrClient.Get(ctx, "default", metav1.GetOptions{})
-			// 	if err != nil {
-			// 		return fmt.Errorf("IstioCSR not found: %v", err))
-			// 	}
-			//
-			// 	status, found, err := unstructured.NestedMap(customResource.Object, "status")
-			// 	if err != nil {
-			// 		return fmt.Errorf("failed to extract status: %v", err))
-			// 	}
-			// 	if !found {
-			// 		return fmt.Errorf("status not found in IstioCSR"))
-			// 	}
-			//
-			// 	// Print status for debugging
-			// 	fmt.Printf("DEBUG: IstioCSR status: %+v\n", status)
-			// 	return nil
-			// }, "30s", "5s").Should(Succeed(), "Should be able to get IstioCSR status for debugging")

459-468: Strengthen assertion: also verify watch-label value

Confirms correct value format, not just presence.

-			Eventually(func() bool {
+			Eventually(func() bool {
 				sourceConfigMap, err := clientset.CoreV1().ConfigMaps(caCertConfigMap.Namespace).Get(ctx, caCertConfigMap.Name, metav1.GetOptions{})
 				if err != nil {
 					return false
 				}
-				_, hasWatchLabel := sourceConfigMap.Labels[testutils.IstioCSRResourceWatchLabelName]
-				return hasWatchLabel
+				val, ok := sourceConfigMap.Labels[testutils.IstioCSRResourceWatchLabelName]
+				expected := fmt.Sprintf("%s_%s", ns.Name, "default")
+				return ok && val == expected
 			}, "1m", "5s").Should(BeTrue(), "Source ConfigMap should have watch label")

498-573: Mirror label-value check for custom-namespace scenario

Same rationale as above; ensure value correctness when source is outside IstioCSR namespace.

-			Eventually(func() bool {
+			Eventually(func() bool {
 				sourceConfigMap, err := clientset.CoreV1().ConfigMaps(customNamespace.Name).Get(ctx, caCertConfigMap.Name, metav1.GetOptions{})
 				if err != nil {
 					return false
 				}
-				_, hasWatchLabel := sourceConfigMap.Labels[testutils.IstioCSRResourceWatchLabelName]
-				return hasWatchLabel
+				val, ok := sourceConfigMap.Labels[testutils.IstioCSRResourceWatchLabelName]
+				expected := fmt.Sprintf("%s_%s", ns.Name, "default")
+				return ok && val == expected
 			}, "1m", "5s").Should(BeTrue(), "Source ConfigMap in custom namespace should have watch label")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between dff9e11 and 316b148.

📒 Files selected for processing (5)
  • pkg/controller/istiocsr/constants.go (1 hunks)
  • pkg/controller/istiocsr/test_utils.go (3 hunks)
  • test/e2e/istio_csr_test.go (4 hunks)
  • test/e2e/testdata/istio/istiocsr_with_ca_certificate.yaml (1 hunks)
  • test/e2e/testdata/istio/istiocsr_with_ca_certificate_custom_namespace.yaml (1 hunks)
🔇 Additional comments (3)
pkg/controller/istiocsr/constants.go (1)

86-96: Expose constants for tests — LGTM

Exporting these constants unblocks e2e usage without leaking impl details. Names and values align with internal counterparts.

test/e2e/testdata/istio/istiocsr_with_ca_certificate.yaml (1)

13-16: Manifest looks correct for ConfigMap-based CA reference

Fields name/key align with new API; namespace implicitly defaults to IstioCSR namespace.

test/e2e/testdata/istio/istiocsr_with_ca_certificate_custom_namespace.yaml (1)

12-15: Covers custom-namespace CA source — LGTM

Template placeholders make this reusable across tests.

@chiragkyal chiragkyal force-pushed the istio-ca-config branch 2 times, most recently from 5e4c7c3 to 2ec4c4b Compare September 15, 2025 07:30
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
test/e2e/istio_csr_test.go (1)

355-355: Remove focused spec: FContext will break CI

FContext found at Line 355 — change to Context.

- FContext("with CA Certificate ConfigMap", func() {
+ Context("with CA Certificate ConfigMap", func() {

Run to ensure no other focused specs remain:

#!/bin/bash
find . -type f -name '*.go' -not -path './vendor/*' -not -path './.git/*' -print0 | xargs -0 -r grep -nHE 'FDescribe\(|FContext\(|FIt\(|FWhen\(|FSpecify\(' || true
🧹 Nitpick comments (4)
test/e2e/istio_csr_test.go (4)

13-13: Consider removing or using the debug log import.

The log package is imported but only used for debugging output. Consider either removing it if not needed in production tests or using a proper test logger.

If debug logging is needed, consider using Ginkgo's built-in GinkgoWriter instead:

-import (
-	"log"
-)

// Then replace log.Printf with:
// fmt.Fprintf(GinkgoWriter, "IstioCSR status: %+v\n", istioCSRStatus)

Also applies to: 22-22


435-435: Replace debug log statements with proper test logging.

Debug log statements using log.Printf should use Ginkgo's built-in logging for better test output management.

-log.Printf("IstioCSR status: %+v", istioCSRStatus)
+fmt.Fprintf(GinkgoWriter, "IstioCSR status: %+v\n", istioCSRStatus)

Also applies to: 542-542


404-407: Remove commented-out code.

Commented verification code should be removed or uncommented if needed.

-			// By("Verifying ConfigMap exists before creating IstioCSR")
-			// _, err = clientset.CoreV1().ConfigMaps(ns.Name).Get(ctx, configMapRefName, metav1.GetOptions{})
-			// Expect(err).ShouldNot(HaveOccurred(), "ConfigMap should exist before creating IstioCSR")
-

413-433: Remove or activate commented debugging code.

Large block of commented debugging code should be removed or properly integrated if debugging is needed.

If this debugging is needed, consider extracting it to a helper function that can be conditionally enabled:

func debugIstioCSRStatus(ctx context.Context, loader *TestLoader, ns string) {
    if os.Getenv("DEBUG_E2E") != "" {
        // debugging code here
    }
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 316b148 and 5e4c7c3.

📒 Files selected for processing (7)
  • pkg/controller/istiocsr/constants.go (1 hunks)
  • pkg/controller/istiocsr/controller.go (2 hunks)
  • pkg/controller/istiocsr/deployments.go (9 hunks)
  • pkg/controller/istiocsr/test_utils.go (3 hunks)
  • test/e2e/istio_csr_test.go (3 hunks)
  • test/e2e/testdata/istio/istiocsr_with_ca_certificate.yaml (1 hunks)
  • test/e2e/testdata/istio/istiocsr_with_ca_certificate_custom_namespace.yaml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • pkg/controller/istiocsr/constants.go
  • test/e2e/testdata/istio/istiocsr_with_ca_certificate_custom_namespace.yaml
  • test/e2e/testdata/istio/istiocsr_with_ca_certificate.yaml
  • pkg/controller/istiocsr/test_utils.go
🔇 Additional comments (16)
pkg/controller/istiocsr/controller.go (3)

193-193: LGTM! Constant renaming for public visibility.

The change from the internal constant to the exported IstiocsrResourceWatchLabelName is consistent with making this label name publicly accessible for external use.


199-199: LGTM! Consistent usage of the exported constant.

The error message correctly uses the newly exported constant name, maintaining consistency throughout the codebase.


230-230: LGTM! Predicate correctly updated to use exported constant.

The watch predicate appropriately uses the exported IstiocsrResourceWatchLabelName constant for filtering resources that the controller should watch.

test/e2e/istio_csr_test.go (3)

387-468: Well-structured test for CA certificate from same namespace.

The test properly validates:

  • CA certificate creation and validation
  • IstioCSR resource creation and readiness
  • CA data copying to the IstioCSR namespace
  • Watch label application

Good test coverage!


470-496: Good negative test case for invalid CA certificate.

The test correctly validates that IstioCSR fails to become ready when provided with a non-CA certificate, ensuring proper certificate validation is in place.


498-573: Comprehensive test for cross-namespace CA certificate usage.

The test thoroughly validates:

  • Custom namespace creation and cleanup
  • CA certificate retrieval from a different namespace
  • Proper copying of CA data across namespaces
  • Watch label application in the source namespace

Excellent coverage of the cross-namespace scenario!

pkg/controller/istiocsr/deployments.go (10)

4-5: LGTM! Necessary imports for certificate validation.

The addition of crypto/x509 and encoding/pem packages is appropriate for PEM data parsing and CA certificate validation.


158-158: LGTM! Consistent usage of exported constant.

The argument correctly uses the exported IstiocsrCAKeyName constant for the root CA file path.


267-282: Well-structured dual CA provisioning logic.

The implementation correctly prioritizes user-provided CA certificates over issuer-based ones, providing a clear fallback mechanism. The error handling properly distinguishes between the two paths.


284-332: Robust user-provided CA handling with comprehensive validation.

The function properly:

  • Validates ConfigMap existence and key presence
  • Performs PEM and CA certificate validation
  • Handles namespace resolution (defaults to IstioCSR namespace)
  • Applies watch labels for tracking
  • Creates/updates the CA ConfigMap copy

Excellent error handling with appropriate use of NewIrrecoverableError for configuration issues.


493-527: Thorough PEM and CA certificate validation.

The validation function comprehensively checks:

  • Non-empty PEM data
  • Valid PEM block structure
  • Certificate type verification
  • CA certificate requirements (IsCA, BasicConstraintsValid)
  • Certificate signing key usage

This ensures only valid CA certificates are accepted.


385-386: LGTM! Consistent usage of exported constants.

The ConfigMap volume source correctly uses the exported constants IstiocsrCAConfigMapName and IstiocsrCAKeyName.

Also applies to: 389-390


529-542: Generic watch label implementation with proper persistence.

The refactored function now accepts any client.Object, making it reusable for different resource types. The implementation correctly:

  • Initializes labels if nil
  • Applies the watch label with proper formatting
  • Persists changes using UpdateWithRetry

Good abstraction!


450-491: Well-structured CA ConfigMap creation/update logic.

The function properly handles both creation and update scenarios with:

  • Existence checking
  • Change detection using hasObjectChanged
  • Appropriate event recording
  • Consistent error handling

The implementation ensures the CA ConfigMap is always in the desired state.


335-361: Consistent issuer-based CA handling.

The function maintains compatibility with the existing issuer-based CA provisioning while properly integrating with the new unified CA ConfigMap approach.


446-447: LGTM! Consistent usage of exported constants throughout.

All references to the CA ConfigMap name and key consistently use the exported constants IstiocsrCAConfigMapName and IstiocsrCAKeyName.

Also applies to: 453-454, 469-469

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
test/e2e/istio_csr_test.go (1)

355-355: Remove focused spec (FContext) — will break CI.

Switch to Context and ensure no other focused nodes remain.

Apply:

-	FContext("with CA Certificate ConfigMap", func() {
+	Context("with CA Certificate ConfigMap", func() {
#!/bin/bash
set -euo pipefail
echo "Searching for focused Ginkgo nodes..."
PAT='\b(FDescribe|FContext|FIt|FWhen|FSpecify)\b'
rg -nP "$PAT" -g '!**/vendor/**' -C1 || true
🧹 Nitpick comments (8)
bundle/manifests/operator.openshift.io_istiocsrs.yaml (1)

1031-1051: Add immutability + basic length validation for istioCACertificate.

Prevents mid‑lifecycle drift and empty keys.

Apply:

                       istioCACertificate:
                         description: |-
                           istioCACertificate when provided, the operator will use the CA certificate from the specified ConfigMap. If empty, the operator will
                           automatically extract the CA certificate from the Secret containing the istiod certificate obtained from cert-manager.
                         properties:
                           key:
                             description: key name holding the required data.
                             type: string
+                            minLength: 1
                           name:
                             description: name of the ConfigMap.
                             type: string
+                            minLength: 1
                           namespace:
                             description: namespace in which the ConfigMap exists.
                               If empty, ConfigMap will be looked up in the IstioCSR
                               namespace.
                             type: string
                         required:
                         - key
                         - name
                         type: object
+                        x-kubernetes-validations:
+                        - message: istioCACertificate is immutable once set
+                          rule: self == oldSelf
pkg/controller/istiocsr/constants.go (2)

72-75: Fix comment to match actual label value format (underscore, not slash).

Avoid confusing consumers/tools.

Apply:

-	// istiocsrResourceWatchLabelName is the value format assigned to istiocsrResourceWatchLabelName label, which
-	// will be of the form <istiocsr_namespace>/<istiocsr_instance-Name>
+	// istiocsrResourceWatchLabelValueFmt is the value format assigned to IstiocsrResourceWatchLabelName label,
+	// of the form <istiocsr_namespace>_<istiocsr_instance-name>.

76-83: Polish constant comments (typo/casing).

Improves clarity; no behavior change.

Apply:

-	// istiocsrCAConfigMapName is the name o the configmap which is mounted in istiocsr container, containing the
-	// CA certificate configured in the secret referenced in the issuer.
+	// istiocsrCAConfigMapName is the name of the ConfigMap mounted in the istiocsr container,
+	// containing the CA certificate configured in the secret referenced by the issuer.
test/e2e/istio_csr_test.go (2)

13-13: Prefer GinkgoWriter over log.Printf for test output.

Keeps logs scoped to the spec.

Apply:

-	"log"
+	// prefer GinkgoWriter for test logs

And replace calls:

-			log.Printf("IstioCSR status: %+v", istioCSRStatus)
+			fmt.Fprintf(GinkgoWriter, "IstioCSR status: %+v\n", istioCSRStatus)

Do the same at Line 542.


470-496: Failure-path test relies on pollTillIstioCSRAvailable error semantics.

If poll returns transient nil before failing, Consistently may pass falsely. Consider asserting Deployment never becomes Available.

Possible alternative:

  • Check DeploymentAvailable condition stays false/absent for the duration.
pkg/controller/istiocsr/deployments.go (2)

539-539: Fix double space in error message format

The error message format string contains "%s resource" which will result in a double space when the object name is empty.

Apply this diff to fix the formatting:

-	return fmt.Errorf("failed to update %s resource with watch label: %w", obj.GetName(), err)
+	return fmt.Errorf("failed to update resource %s with watch label: %w", obj.GetName(), err)

335-361: Consider adding validation for issuer-based CA certificates

While user-provided CA certificates undergo thorough PEM validation via validatePEMData, the issuer-based CA path doesn't perform the same validation on the certificate data from the secret.

Apply this diff to add validation:

 func (r *Reconciler) handleIssuerBasedCA(deployment *appsv1.Deployment, istiocsr *v1alpha1.IstioCSR, resourceLabels map[string]string) error {
 	var (
 		issuerConfig certmanagerv1.IssuerConfig
 	)
 
 	obj, err := r.getIssuer(istiocsr)
 	if err != nil {
 		return FromClientError(err, "failed to fetch issuer")
 	}
 
 	issuerRefKind := strings.ToLower(istiocsr.Spec.IstioCSRConfig.CertManager.IssuerRef.Kind)
 	switch issuerRefKind {
 	case clusterIssuerKind:
 		issuerConfig = obj.(*certmanagerv1.ClusterIssuer).Spec.IssuerConfig
 	case issuerKind:
 		issuerConfig = obj.(*certmanagerv1.Issuer).Spec.IssuerConfig
 	}
 
 	if issuerConfig.CA != nil && issuerConfig.CA.SecretName != "" {
 		if err := r.createCAConfigMapFromIssuerSecret(istiocsr, issuerConfig, resourceLabels); err != nil {
 			return FromClientError(err, "failed to create CA ConfigMap")
 		}
 		updateVolumeWithIssuerCA(deployment)
 	}
 
 	return nil
 }

And in createCAConfigMapFromIssuerSecret:

 func (r *Reconciler) createCAConfigMapFromIssuerSecret(istiocsr *v1alpha1.IstioCSR, issuerConfig certmanagerv1.IssuerConfig, resourceLabels map[string]string) error {
 	if issuerConfig.CA == nil || issuerConfig.CA.SecretName == "" {
 		return nil
 	}
 
 	secretKey := types.NamespacedName{
 		Name:      issuerConfig.CA.SecretName,
 		Namespace: istiocsr.Spec.IstioCSRConfig.Istio.Namespace,
 	}
 	secret := &corev1.Secret{}
 	if err := r.Get(r.ctx, secretKey, secret); err != nil {
 		return fmt.Errorf("failed to fetch secret in issuer: %w", err)
 	}
 	if err := r.updateWatchLabel(secret, istiocsr); err != nil {
 		return err
 	}
 
 	certData := string(secret.Data[IstiocsrCAKeyName])
+	// Validate the certificate data from the issuer secret
+	if err := r.validatePEMData(certData); err != nil {
+		return fmt.Errorf("invalid CA certificate in issuer secret %s/%s: %w", secretKey.Namespace, secretKey.Name, err)
+	}
 	return r.createOrUpdateCAConfigMap(istiocsr, certData, resourceLabels)
 }
pkg/controller/istiocsr/deployments_test.go (1)

450-450: Fix double-space in error message when object name is empty

pkg/controller/istiocsr/deployments.go:539 uses "failed to update %s resource with watch label" — when obj.GetName() is empty this emits two spaces (seen in pkg/controller/istiocsr/deployments_test.go:450). Update the formatting to omit the extra space or conditionally include the name.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 5e4c7c3 and 2ec4c4b.

📒 Files selected for processing (15)
  • api/operator/v1alpha1/istiocsr_types.go (1 hunks)
  • api/operator/v1alpha1/zz_generated.deepcopy.go (3 hunks)
  • bundle/manifests/operator.openshift.io_istiocsrs.yaml (1 hunks)
  • config/crd/bases/operator.openshift.io_istiocsrs.yaml (1 hunks)
  • pkg/controller/istiocsr/constants.go (1 hunks)
  • pkg/controller/istiocsr/controller.go (2 hunks)
  • pkg/controller/istiocsr/deployments.go (9 hunks)
  • pkg/controller/istiocsr/deployments_test.go (2 hunks)
  • pkg/controller/istiocsr/test_utils.go (3 hunks)
  • pkg/operator/applyconfigurations/operator/v1alpha1/certmanagerconfig.go (2 hunks)
  • pkg/operator/applyconfigurations/operator/v1alpha1/configmapreference.go (1 hunks)
  • pkg/operator/applyconfigurations/utils.go (1 hunks)
  • test/e2e/istio_csr_test.go (3 hunks)
  • test/e2e/testdata/istio/istiocsr_with_ca_certificate.yaml (1 hunks)
  • test/e2e/testdata/istio/istiocsr_with_ca_certificate_custom_namespace.yaml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • pkg/operator/applyconfigurations/operator/v1alpha1/configmapreference.go
  • test/e2e/testdata/istio/istiocsr_with_ca_certificate.yaml
  • api/operator/v1alpha1/zz_generated.deepcopy.go
  • pkg/controller/istiocsr/test_utils.go
  • pkg/operator/applyconfigurations/utils.go
  • pkg/controller/istiocsr/controller.go
  • api/operator/v1alpha1/istiocsr_types.go
  • config/crd/bases/operator.openshift.io_istiocsrs.yaml
🔇 Additional comments (18)
pkg/operator/applyconfigurations/operator/v1alpha1/certmanagerconfig.go (2)

30-36: Fluent setter is consistent.

Nil value clears the field; OK for apply configs.


12-14: API surface addition verified — apply-config and wiring present.

  • ConfigMapReferenceApplyConfiguration: pkg/operator/applyconfigurations/operator/v1alpha1/configmapreference.go
  • CertManagerConfigApplyConfiguration references it (WithIstioCACertificate): pkg/operator/applyconfigurations/operator/v1alpha1/certmanagerconfig.go
  • ForKind factory wired: pkg/operator/applyconfigurations/utils.go
  • API type/field exist and deepcopy updated: api/operator/v1alpha1/istiocsr_types.go
test/e2e/testdata/istio/istiocsr_with_ca_certificate_custom_namespace.yaml (1)

1-20: Good coverage for cross-namespace reference.

Template placeholders and required fields look correct.

test/e2e/istio_csr_test.go (2)

387-468: Happy path e2e looks sound; asserts check copy + label.

Good use of Eventually/intervals and constant reuse.

Please confirm controller sets the watch label key to testutils.IstiocsrResourceWatchLabelName in all copy paths (same/custom namespace).


498-573: Custom-namespace flow is well covered.

Data equality and label presence checks are appropriate.

pkg/controller/istiocsr/deployments_test.go (8)

713-746: LGTM! Clean implementation of CA certificate ConfigMap testing

The test case properly validates the successful reconciliation path when a CA certificate ConfigMap is provided. The mock setup correctly simulates the creation flow and the update scenario validates the expected behavior.


748-779: LGTM! Proper handling of CA certificate in custom namespace

The test correctly validates that CA certificates can be fetched from a custom namespace when specified in the configuration.


781-809: LGTM! Appropriate error handling for missing CA certificate

The test properly validates that a clear error message is returned when the CA certificate ConfigMap is not found.


811-842: LGTM! Validates missing key scenario correctly

The test ensures that missing keys in the CA certificate ConfigMap are properly detected and reported with a descriptive error message.


844-875: LGTM! Comprehensive PEM validation testing

The test properly validates that invalid PEM data is rejected with an appropriate error message.


878-922: LGTM! Proper error handling for watch label update failures

The test correctly validates that failures when updating watch labels on CA certificate ConfigMaps are properly handled and reported.


925-954: LGTM! Validates non-CA certificate rejection

The test ensures that certificates without the CA flag are properly rejected with a clear error message.


957-986: LGTM! Validates certificate signing key usage requirement

The test properly ensures that CA certificates without the KeyUsageCertSign flag are rejected with an appropriate error message.

pkg/controller/istiocsr/deployments.go (5)

267-282: LGTM! Clean dual CA provisioning design

The implementation provides a clear priority system where user-provided CA certificates take precedence over issuer-based ones. The separation of concerns between handleUserProvidedCA and handleIssuerBasedCA makes the code maintainable.


284-332: LGTM! Comprehensive user-provided CA handling

The function properly validates the CA certificate ConfigMap, including namespace resolution, key existence, PEM validation, and watch label application. The error messages are descriptive and helpful for debugging.


493-527: LGTM! Thorough PEM and CA certificate validation

The validation ensures:

  1. Valid PEM format
  2. Certificate type is "CERTIFICATE"
  3. Certificate has CA flag set
  4. Valid Basic Constraints extension
  5. Certificate Sign key usage

This provides robust validation for CA certificates.


529-542: LGTM! Generic resource labeling implementation

The refactored function now accepts any client.Object, making it reusable for both Secrets and ConfigMaps. This is a good abstraction that reduces code duplication.


450-491: LGTM! Well-structured CA ConfigMap management

The createOrUpdateCAConfigMap function properly handles both creation and update scenarios, with appropriate event recording and logging. The consolidation of CA ConfigMap management logic reduces code duplication.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
test/e2e/istio_csr_test.go (1)

1-576: Sanity check: focused Ginkgo nodes should be absent.

Past reviews flagged FContext; looks resolved here. Run this to verify repo-wide.

#!/bin/bash
set -euo pipefail
echo "Scanning for focused Ginkgo nodes..."
PATTERN='\b(FDescribe|FContext|FIt|FWhen|FSpecify)\b'
if command -v rg >/dev/null 2>&1; then
  rg -nP "$PATTERN" -g '!**/vendor/**' -S || true
else
  grep -RInE "$PATTERN" --exclude-dir vendor --include '*.go' . || true
fi
🧹 Nitpick comments (11)
pkg/controller/istiocsr/constants.go (3)

68-75: Fix exported-const doc and label value format in comment.

Start the doc with the exported name and avoid “/” in examples since label values cannot contain slashes.

-// istiocsrResourceWatchLabelName is the label name for identifying the resources of interest for the
-// controller but does not create or manage the resource.
+// IstiocsrResourceWatchLabelName is the label used to mark resources of interest
+// that the controller watches but does not manage.
 
-// istiocsrResourceWatchLabelName is the value format assigned to istiocsrResourceWatchLabelName label, which
-// will be of the form <istiocsr_namespace>/<istiocsr_instance-Name>
+// istiocsrResourceWatchLabelValueFmt is the value format assigned to
+// IstiocsrResourceWatchLabelName; it is "<istiocsr_namespace>_<istiocsr_instance_name>".

76-79: Fix typos and exported-const doc.

Capitalize ConfigMap and start with the exported name.

-// istiocsrCAConfigMapName is the name o the configmap which is mounted in istiocsr container, containing the
-// CA certificate configured in the secret referenced in the issuer.
+// IstiocsrCAConfigMapName is the name of the ConfigMap mounted in the istiocsr
+// container that contains the CA certificate copied from the issuer or user ConfigMap.

80-83: Align comment with exported name and proper noun.

-// istiocsrCAKeyName is the key name holding the CA certificate in the issuer secret or the controller
-// CA configmap.
+// IstiocsrCAKeyName is the key holding the CA certificate in the issuer Secret
+// or the controller-owned CA ConfigMap.
pkg/controller/istiocsr/deployments.go (6)

142-177: Root CA arg is always set; deployment may fail if no CA gets mounted.

If neither user-provided CA nor issuer-based CA resolves, --root-ca-file points to a non-existent path. Either gate the arg or hard-fail when no CA is configured.

Option A (fail fast in issuer path when no CA configured):

 func (r *Reconciler) handleIssuerBasedCA(deployment *appsv1.Deployment, istiocsr *v1alpha1.IstioCSR, resourceLabels map[string]string) error {
@@
- if issuerConfig.CA != nil && issuerConfig.CA.SecretName != "" {
+ if issuerConfig.CA != nil && issuerConfig.CA.SecretName != "" {
     if err := r.createCAConfigMapFromIssuerSecret(istiocsr, issuerConfig, resourceLabels); err != nil {
       return FromClientError(err, "failed to create CA ConfigMap")
     }
     updateVolumeWithIssuerCA(deployment)
- }
-
- return nil
+   return nil
+ }
+ return NewIrrecoverableError(fmt.Errorf("no CA configured"),
+   "neither spec.istioCSRConfig.certManager.istioCACertificate nor issuer CA.secretName is configured")
 }

Option B (conditionally add arg only when CA is mounted): compute args first, then append --root-ca-file after updateVolumes determines CA presence. That would require moving arg construction or setting an env var/annotation to signal presence.


266-282: Short-circuit returns skip issuer path errors.

If user CA is nil, you fall back; if both are empty, you still return nil. Prefer surfacing an irrecoverable error so status reflects misconfiguration (ties to prior comment).

-  if err := r.handleIssuerBasedCA(deployment, istiocsr, resourceLabels); err != nil {
-    return err
-  }
-
-  return nil
+  return r.handleIssuerBasedCA(deployment, istiocsr, resourceLabels)

284-333: Validate ConfigMapReference fields early and validate PEM chains.

  • Guard against empty name/key for clearer errors.
  • You already validate PEM; keep it, but also accept PEM bundles in validatePEMData (see separate comment).
 func (r *Reconciler) handleUserProvidedCA(deployment *appsv1.Deployment, istiocsr *v1alpha1.IstioCSR, resourceLabels map[string]string) error {
   caCertConfig := istiocsr.Spec.IstioCSRConfig.CertManager.IstioCACertificate
   if caCertConfig == nil {
     return NewIrrecoverableError(fmt.Errorf("CA certificate configuration is nil"), "CA certificate validation failed")
   }
+  if caCertConfig.Name == "" || caCertConfig.Key == "" {
+    return NewIrrecoverableError(fmt.Errorf("invalid CA certificate reference"),
+      "ConfigMap name and key must be non-empty")
+  }
@@
   if _, exists := sourceConfigMap.Data[caCertConfig.Key]; !exists {
     return NewIrrecoverableError(fmt.Errorf("key %q not found in ConfigMap %s/%s", caCertConfig.Key, sourceConfigMapKey.Namespace, sourceConfigMapKey.Name), "CA certificate ConfigMap %s/%s does not contain required key %q", sourceConfigMapKey.Namespace, sourceConfigMapKey.Name, caCertConfig.Key)
   }
 
   // Validate that the key contains PEM-formatted content
   pemData := sourceConfigMap.Data[caCertConfig.Key]
   if err := r.validatePEMData(pemData); err != nil {
     return NewIrrecoverableError(err, "invalid PEM data in CA certificate ConfigMap %s/%s key %q", sourceConfigMapKey.Namespace, sourceConfigMapKey.Name, caCertConfig.Key)
   }

363-406: Don’t clobber existing volumes/mounts; merge instead.

Replacing the entire lists risks dropping unrelated mounts from the base manifest.

-  for i, container := range deployment.Spec.Template.Spec.Containers {
+  for i, container := range deployment.Spec.Template.Spec.Containers {
     if container.Name == istiocsrContainerName {
-      deployment.Spec.Template.Spec.Containers[i].VolumeMounts = volumeMounts
+      // ensure mount present without removing others
+      mounts := deployment.Spec.Template.Spec.Containers[i].VolumeMounts
+      present := false
+      for _, m := range mounts {
+        if m.Name == caVolumeName {
+          present = true
+          break
+        }
+      }
+      if !present {
+        mounts = append(mounts, volumeMounts...)
+      }
+      deployment.Spec.Template.Spec.Containers[i].VolumeMounts = mounts
     }
   }
-  deployment.Spec.Template.Spec.Volumes = volumes
+  // ensure volume present without removing others
+  existing := deployment.Spec.Template.Spec.Volumes
+  vPresent := false
+  for _, v := range existing {
+    if v.Name == caVolumeName {
+      vPresent = true
+      break
+    }
+  }
+  if !vPresent {
+    existing = append(existing, volumes...)
+  }
+  deployment.Spec.Template.Spec.Volumes = existing

429-448: Validate issuer Secret data and PEM before copying.

Avoid writing empty/invalid data into the ConfigMap.

   if err := r.updateWatchLabel(secret, istiocsr); err != nil {
     return err
   }
 
-  certData := string(secret.Data[IstiocsrCAKeyName])
-  return r.createOrUpdateCAConfigMap(istiocsr, certData, resourceLabels)
+  data, ok := secret.Data[IstiocsrCAKeyName]
+  if !ok || len(data) == 0 {
+    return NewIrrecoverableError(fmt.Errorf("issuer secret missing %q", IstiocsrCAKeyName),
+      "issuer Secret %s/%s does not contain a CA certificate", secret.Namespace, secret.Name)
+  }
+  certData := string(data)
+  if err := r.validatePEMData(certData); err != nil {
+    return NewIrrecoverableError(err, "invalid PEM data in issuer Secret %s/%s key %q", secret.Namespace, secret.Name, IstiocsrCAKeyName)
+  }
+  return r.createOrUpdateCAConfigMap(istiocsr, certData, resourceLabels)

493-527: Support PEM bundles (multiple certs) in validation.

Many CA bundles contain multiple certs; iterate over all PEM blocks and require each is a CA certificate.

-func (r *Reconciler) validatePEMData(pemData string) error {
+func (r *Reconciler) validatePEMData(pemData string) error {
   if pemData == "" {
     return fmt.Errorf("PEM data is empty")
   }
 
-  // Parse the first certificate from PEM data
-  block, _ := pem.Decode([]byte(pemData))
-  if block == nil {
-    return fmt.Errorf("no valid PEM data found")
-  }
-
-  if block.Type != "CERTIFICATE" {
-    return fmt.Errorf("PEM block is not a certificate, found: %s", block.Type)
-  }
-
-  cert, err := x509.ParseCertificate(block.Bytes)
-  if err != nil {
-    return fmt.Errorf("failed to parse certificate: %w", err)
-  }
-
-  if !cert.IsCA {
-    return fmt.Errorf("certificate is not a CA certificate")
-  }
-
-  if !cert.BasicConstraintsValid {
-    return fmt.Errorf("certificate does not have valid Basic Constraints extension")
-  }
-
-  // Check Key Usage for certificate signing
-  if cert.KeyUsage&x509.KeyUsageCertSign == 0 {
-    return fmt.Errorf("certificate does not have Certificate Sign key usage")
-  }
+  rest := []byte(pemData)
+  found := false
+  for {
+    block, r := pem.Decode(rest)
+    if block == nil {
+      break
+    }
+    rest = r
+    if block.Type != "CERTIFICATE" {
+      return fmt.Errorf("PEM block is not a certificate, found: %s", block.Type)
+    }
+    cert, err := x509.ParseCertificate(block.Bytes)
+    if err != nil {
+      return fmt.Errorf("failed to parse certificate: %w", err)
+    }
+    found = true
+    if !cert.IsCA || !cert.BasicConstraintsValid || (cert.KeyUsage&x509.KeyUsageCertSign) == 0 {
+      return fmt.Errorf("PEM contains a non-CA certificate or missing cert-sign key usage")
+    }
+  }
+  if !found {
+    return fmt.Errorf("no valid PEM certificate blocks found")
+  }
   return nil
 }
test/e2e/istio_csr_test.go (2)

13-13: Prefer GinkgoWriter over log.Printf for test output.

This integrates with Ginkgo’s reporting and parallel runs.

-   "log"
+   . "github.com/onsi/ginkgo/v2"
@@
-   log.Printf("IstioCSR status: %+v", istioCSRStatus)
+   GinkgoWriter.Printf("IstioCSR status: %+v\n", istioCSRStatus)
@@
-   log.Printf("IstioCSR status: %+v", istioCSRStatus)
+   GinkgoWriter.Printf("IstioCSR status: %+v\n", istioCSRStatus)

355-574: Decouple e2e from controller internals.

Importing pkg/controller/istiocsr to fetch constants tightens coupling; consider duplicating string constants in e2e or exposing them via an API/test package to avoid breaking tests on controller refactors.

If you keep the import, please confirm no circular deps and that the package remains stable across branches.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 2ec4c4b and b278e61.

📒 Files selected for processing (7)
  • pkg/controller/istiocsr/constants.go (1 hunks)
  • pkg/controller/istiocsr/controller.go (2 hunks)
  • pkg/controller/istiocsr/deployments.go (9 hunks)
  • pkg/controller/istiocsr/test_utils.go (3 hunks)
  • test/e2e/istio_csr_test.go (3 hunks)
  • test/e2e/testdata/istio/istiocsr_with_ca_certificate.yaml (1 hunks)
  • test/e2e/testdata/istio/istiocsr_with_ca_certificate_custom_namespace.yaml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • test/e2e/testdata/istio/istiocsr_with_ca_certificate.yaml
  • test/e2e/testdata/istio/istiocsr_with_ca_certificate_custom_namespace.yaml
  • pkg/controller/istiocsr/test_utils.go
  • pkg/controller/istiocsr/controller.go

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
test/e2e/istio_csr_test.go (2)

481-485: Randomize custom namespace to avoid AlreadyExists flakes.

Use a randomized testing namespace to prevent cross-run collisions if cleanup is skipped.

-      customNamespace, err := loader.CreateTestingNS("custom-ca-ns", false)
+      customNamespace, err := loader.CreateTestingNS("custom-ca-ns", true)

22-22: Avoid e2e importing controller internals for constants.

Pull shared constants into a dedicated public package (e.g., pkg/constants) to decouple tests from controller internals. Low priority but improves layering.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 8ccc7f1 and 6820b96.

📒 Files selected for processing (2)
  • test/e2e/istio_csr_test.go (3 hunks)
  • test/e2e/testdata/istio/istiocsr_with_ca_certificate_template.yaml (1 hunks)
🧰 Additional context used
🪛 YAMLlint (1.37.1)
test/e2e/testdata/istio/istiocsr_with_ca_certificate_template.yaml

[error] 14-14: syntax error: expected , but found '{'

(syntax)

@chiragkyal
Copy link
Member Author

/retest

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
test/e2e/testdata/istio/istiocsr_with_ca_certificate_template.yaml (1)

14-16: Fix template/YAML break: move the conditional off the name: line.

Inline {{ if ... }} after name: makes the YAML invalid (yamllint error at Line 14). Emit the conditional on separate lines so the template renders valid keys.

Apply this diff:

       istioCACertificate:
-        name: {{.ConfigMapName}}{{- if .CustomNamespace}}
-        namespace: {{.CustomNamespace}}{{- end}}
-        key: {{.ConfigMapKey}}
+        name: {{ .ConfigMapName }}
+{{- if .CustomNamespace }}
+        namespace: {{ .CustomNamespace }}
+{{- end }}
+        key: {{ .ConfigMapKey }}
🧹 Nitpick comments (1)
test/e2e/testdata/istio/istiocsr_with_ca_certificate_template.yaml (1)

14-16: Minor template hygiene: add spaces and consider quoting.

Spaces inside delimiters improve readability; quoting values is optional but harmless.

-        name: {{ .ConfigMapName }}
+        name: "{{ .ConfigMapName }}"
 {{- if .CustomNamespace }}
-        namespace: {{ .CustomNamespace }}
+        namespace: "{{ .CustomNamespace }}"
 {{- end }}
-        key: {{ .ConfigMapKey }}
+        key: "{{ .ConfigMapKey }}"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 6820b96 and d166849.

📒 Files selected for processing (2)
  • test/e2e/istio_csr_test.go (3 hunks)
  • test/e2e/testdata/istio/istiocsr_with_ca_certificate_template.yaml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/e2e/istio_csr_test.go
🧰 Additional context used
🪛 YAMLlint (1.37.1)
test/e2e/testdata/istio/istiocsr_with_ca_certificate_template.yaml

[error] 14-14: syntax error: expected , but found '{'

(syntax)

🔇 Additional comments (1)
test/e2e/testdata/istio/istiocsr_with_ca_certificate_template.yaml (1)

1-13: LGTM on structure and field names.

Spec shape and indentation look consistent with the CRD expectations.

Also applies to: 17-20

@chiragkyal
Copy link
Member Author

/retest

Comment on lines 285 to 288
caCertConfig := istiocsr.Spec.IstioCSRConfig.CertManager.IstioCACertificate
if caCertConfig == nil {
return NewIrrecoverableError(fmt.Errorf("CA certificate configuration is nil"), "CA certificate validation failed")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This should not be failed, IstioCACertificate is not mandatory field.

Copy link
Member Author

Choose a reason for hiding this comment

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

Removed the if condition check from here, because handleUserProvidedCA function will be called only when caCertConfig is not-nil


// Validate that the specified key exists in the ConfigMap
if _, exists := sourceConfigMap.Data[caCertConfig.Key]; !exists {
return NewIrrecoverableError(fmt.Errorf("key %q not found in ConfigMap %s/%s", caCertConfig.Key, sourceConfigMapKey.Namespace, sourceConfigMapKey.Name), "CA certificate ConfigMap %s/%s does not contain required key %q", sourceConfigMapKey.Namespace, sourceConfigMapKey.Name, caCertConfig.Key)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
return NewIrrecoverableError(fmt.Errorf("key %q not found in ConfigMap %s/%s", caCertConfig.Key, sourceConfigMapKey.Namespace, sourceConfigMapKey.Name), "CA certificate ConfigMap %s/%s does not contain required key %q", sourceConfigMapKey.Namespace, sourceConfigMapKey.Name, caCertConfig.Key)
return NewIrrecoverableError(fmt.Errorf("key %q not found in ConfigMap %s/%s", caCertConfig.Key, sourceConfigMapKey.Namespace, sourceConfigMapKey.Name), "invalid spec.istioCSRConfig.certManager.istioCACertificate")

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated the message to

		return NewIrrecoverableError(fmt.Errorf("key %q not found in ConfigMap %s/%s", caCertConfig.Key, sourceConfigMapKey.Namespace, sourceConfigMapKey.Name), "invalid CA certificate ConfigMap %s/%s", sourceConfigMapKey.Namespace, sourceConfigMapKey.Name)

I think we can avoid keeping the field names (spec.istioCSRConfig.certManager.istioCACertificate) directly into the message

Comment on lines 513 to 525
if !cert.IsCA {
return fmt.Errorf("certificate is not a CA certificate")
}

if !cert.BasicConstraintsValid {
return fmt.Errorf("certificate does not have valid Basic Constraints extension")
}

// Check Key Usage for certificate signing
if cert.KeyUsage&x509.KeyUsageCertSign == 0 {
return fmt.Errorf("certificate does not have Certificate Sign key usage")
}

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I don't think we need to check the key usage, which will be verified by the cert-manager. CA is part of the BasicContraints, hence first BasicConstraints check and then IsCA check would be more apt.

Suggested change
if !cert.IsCA {
return fmt.Errorf("certificate is not a CA certificate")
}
if !cert.BasicConstraintsValid {
return fmt.Errorf("certificate does not have valid Basic Constraints extension")
}
// Check Key Usage for certificate signing
if cert.KeyUsage&x509.KeyUsageCertSign == 0 {
return fmt.Errorf("certificate does not have Certificate Sign key usage")
}
if !cert.BasicConstraintsValid {
return fmt.Errorf("certificate does not have valid Basic Constraints extension")
}
if !cert.IsCA {
return fmt.Errorf("certificate is not a CA certificate")
}

Copy link
Member Author

Choose a reason for hiding this comment

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

nit: I don't think we need to check the key usage, which will be verified by the cert-manager.

True, however I think early validation provides much better UX than runtime failures. And the validation is lightweight and doesn't impact performance and also it's consistent with the other CA validations in this function. So if there are no major concerns, we can keep it as is.

Copy link
Member Author

Choose a reason for hiding this comment

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

CA is part of the BasicContraints, hence first BasicConstraints check and then IsCA check would be more apt.

Sure!

@chiragkyal
Copy link
Member Author

/cherrypick cert-manager-1.18

@openshift-cherrypick-robot

@chiragkyal: once the present PR merges, I will cherry-pick it on top of cert-manager-1.18 in a new PR and assign it to you.

In response to this:

/cherrypick cert-manager-1.18

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@openshift-merge-robot openshift-merge-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Oct 7, 2025
@bharath-b-rh
Copy link
Contributor

/cherrypick cert-manager-1.18

@openshift-cherrypick-robot

@bharath-b-rh: once the present PR merges, I will cherry-pick it on top of cert-manager-1.18 in a new PR and assign it to you.

In response to this:

/cherrypick cert-manager-1.18

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@openshift-merge-robot openshift-merge-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Oct 8, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (4)
test/e2e/testdata/istio/istiocsr_with_ca_certificate_template.yaml (1)

13-16: YAML syntax error: move inline conditional to separate lines.

The inline {{- if .CustomNamespace}} on line 14 renders invalid YAML. This issue was previously raised and remains unresolved.

Apply this diff to fix the syntax error and quote template values:

       istioCACertificate:
-        name: {{.ConfigMapName}}{{- if .CustomNamespace}}
-        namespace: {{.CustomNamespace}}{{- end}}
-        key: {{.ConfigMapKey}}
+        name: "{{ .ConfigMapName }}"
+{{- if .CustomNamespace }}
+        namespace: "{{ .CustomNamespace }}"
+{{- end }}
+        key: "{{ .ConfigMapKey }}"
pkg/controller/istiocsr/test_utils.go (2)

5-9: Missing required import for certificate serial numbers.

The crypto/x509 package requires math/big for generating certificate serial numbers, which is used in the GenerateCertificate function below.

Add the import:

 import (
   "context"
   "crypto/rand"
   "crypto/rsa"
   "crypto/x509"
   "crypto/x509/pkix"
   "encoding/pem"
+  "math/big"
   "fmt"
   "testing"
   "time"

274-310: Critical: missing SerialNumber causes panic.

The certificate template lacks a SerialNumber, which causes x509.CreateCertificate to panic at line 298. A cryptographically-random serial number must be set before calling CreateCertificate.

Apply this diff:

   template := &x509.Certificate{
     Subject: pkix.Name{
       CommonName:   commonName,
       Organization: organization,
     },
     NotBefore:             time.Now(),
     NotAfter:              time.Now().Add(24 * time.Hour),
     KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
     ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
     BasicConstraintsValid: true,
     IsCA:                  false,
   }
   
   // Apply tweaks to modify the template
   tweak(template)
   
+  // Set a random serial number
+  serialLimit := new(big.Int).Lsh(big.NewInt(1), 128)
+  serial, err := rand.Int(rand.Reader, serialLimit)
+  if err != nil {
+    panic(fmt.Sprintf("Failed to generate serial number: %v", err))
+  }
+  template.SerialNumber = serial
+  
   certDER, err := x509.CreateCertificate(rand.Reader, template, template, &privateKey.PublicKey, privateKey)
pkg/controller/istiocsr/deployments.go (1)

313-321: Move watch label update before validation.

Adding the watch label after validation (line 318) means validation failures won't trigger reconciliation when the source ConfigMap changes. Move the watch label update before validation so that subsequent ConfigMap changes will trigger a reconcile even after a validation failure.

Based on past review feedback, apply this reordering:

   // Validate that the key contains PEM-formatted content
   pemData := sourceConfigMap.Data[caCertConfig.Key]
-  if err := r.validatePEMData(pemData); err != nil {
-    return NewIrrecoverableError(err, "invalid PEM data in CA certificate ConfigMap %s/%s key %q", sourceConfigMapKey.Namespace, sourceConfigMapKey.Name, caCertConfig.Key)
-  }
   
   // Add watch label to the source ConfigMap for tracking
+  // This is done before validation so that validation failures will trigger reconciliation
+  // when the source ConfigMap is corrected
   if err := r.updateWatchLabel(sourceConfigMap, istiocsr); err != nil {
     return FromClientError(err, "failed to update watch label on CA certificate ConfigMap %s/%s", sourceConfigMapKey.Namespace, sourceConfigMapKey.Name)
   }
+
+  // Validate that the key contains PEM-formatted content
+  if err := r.validatePEMData(pemData); err != nil {
+    return NewIrrecoverableError(err, "invalid PEM data in CA certificate ConfigMap %s/%s key %q", sourceConfigMapKey.Namespace, sourceConfigMapKey.Name, caCertConfig.Key)
+  }
🧹 Nitpick comments (2)
pkg/controller/istiocsr/deployments.go (2)

284-288: Remove redundant nil check.

The nil check at line 286 is redundant because handleUserProvidedCA is only called from line 269 when istiocsr.Spec.IstioCSRConfig.CertManager.IstioCACertificate != nil.

Apply this diff:

 func (r *Reconciler) handleUserProvidedCA(deployment *appsv1.Deployment, istiocsr *v1alpha1.IstioCSR, resourceLabels map[string]string) error {
   caCertConfig := istiocsr.Spec.IstioCSRConfig.CertManager.IstioCACertificate
-  if caCertConfig == nil {
-    return NewIrrecoverableError(fmt.Errorf("CA certificate configuration is nil"), "CA certificate validation failed")
-  }
-
   // Determine the namespace - use specified namespace or default to IstioCSR namespace
   namespace := caCertConfig.Namespace

513-524: Reorder certificate validation checks.

Check BasicConstraintsValid before IsCA for more accurate error reporting. A past review also suggested removing the KeyUsage check as cert-manager will verify it, though retaining it provides defense-in-depth.

Apply this diff:

+  if !cert.BasicConstraintsValid {
+    return fmt.Errorf("certificate does not have valid Basic Constraints extension")
+  }
+
   if !cert.IsCA {
     return fmt.Errorf("certificate is not a CA certificate")
   }
   
-  if !cert.BasicConstraintsValid {
-    return fmt.Errorf("certificate does not have valid Basic Constraints extension")
-  }
-  
   // Check Key Usage for certificate signing
   if cert.KeyUsage&x509.KeyUsageCertSign == 0 {
     return fmt.Errorf("certificate does not have Certificate Sign key usage")
   }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between d166849 and 83d83dc.

📒 Files selected for processing (7)
  • pkg/controller/istiocsr/constants.go (1 hunks)
  • pkg/controller/istiocsr/controller.go (2 hunks)
  • pkg/controller/istiocsr/deployments.go (9 hunks)
  • pkg/controller/istiocsr/deployments_test.go (2 hunks)
  • pkg/controller/istiocsr/test_utils.go (3 hunks)
  • test/e2e/istio_csr_test.go (3 hunks)
  • test/e2e/testdata/istio/istiocsr_with_ca_certificate_template.yaml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/controller/istiocsr/constants.go
  • pkg/controller/istiocsr/controller.go
🧰 Additional context used
🪛 YAMLlint (1.37.1)
test/e2e/testdata/istio/istiocsr_with_ca_certificate_template.yaml

[error] 14-14: syntax error: expected , but found '{'

(syntax)

🔇 Additional comments (3)
pkg/controller/istiocsr/deployments_test.go (1)

712-986: Comprehensive test coverage for CA ConfigMap scenarios.

The new test cases thoroughly validate:

  • CA ConfigMap in same and custom namespaces
  • Missing ConfigMap and key scenarios
  • Invalid PEM data and non-CA certificates
  • Certificate validation (CA flag, CertSign key usage)
  • Watch label update failures

The test structure and mocking patterns are well-implemented.

test/e2e/istio_csr_test.go (1)

354-551: Well-structured e2e tests for CA ConfigMap provisioning.

The new test context validates:

  • CA ConfigMap data copying from same and custom namespaces
  • Watch label addition to source ConfigMaps
  • Non-CA certificate rejection
  • Proper cleanup with AfterEach and defer (correctly placed in It blocks)

The tests use Eventually for async operations and validate both ConfigMap data and labels, ensuring the CA provisioning workflow behaves correctly.

pkg/controller/istiocsr/deployments.go (1)

267-282: Clean refactor for CA provisioning paths.

The split between user-provided CA (handleUserProvidedCA) and issuer-based CA (handleIssuerBasedCA) is clear and maintainable. The validation flow properly checks ConfigMap existence, key presence, and PEM format before creating the CA ConfigMap copy.

Also applies to: 284-361

@bharath-b-rh
Copy link
Contributor

/hold

@openshift-ci openshift-ci bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Oct 19, 2025
@bharath-b-rh
Copy link
Contributor

/lgtm cancel

@openshift-ci openshift-ci bot removed lgtm Indicates that a PR is ready to be merged. approved Indicates a PR has been approved by an approver from all required OWNERS files. labels Oct 19, 2025
@chiragkyal
Copy link
Member Author

chiragkyal commented Oct 20, 2025

Please check the comment for test scenarios.

@bharath-b-rh Don't we need these test scenarios to be added to the QE pipeline for automated testing for all future PRs? Are we tracking this effort somewhere?

/cc @lunarwhite

@openshift-ci openshift-ci bot requested a review from lunarwhite October 20, 2025 12:02
@lunarwhite
Copy link
Member

Don't we need these test scenarios to be added to the QE pipeline for automated testing for all future PRs? Are we tracking this effort somewhere?

The test scenarios desinged by @bharath-b-rh will be recorded in the Polarion for test cases management. FWIW, there will no longer be a separate QE pipeline in the future. There are some org-level initiatives to unify automation test suite and CI, e.g. https://issues.redhat.com/browse/OCPSTRAT-2317 to merge QE test suites into the component repos and establish unified CI jobs. This helps reduce duplicated work, address the inconvenience caused by gaps between different pipelines, and improve the overall maintainability of the CI in the long-term. We can discuss it in one of our bi-weekly sync calls later.

@bharath-b-rh
Copy link
Contributor

/retest

@bharath-b-rh
Copy link
Contributor

/lgtm
/unhold
/approve

@openshift-ci openshift-ci bot added lgtm Indicates that a PR is ready to be merged. and removed do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. labels Oct 20, 2025
Copy link
Contributor

openshift-ci bot commented Oct 20, 2025

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: bharath-b-rh, chiragkyal

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Oct 20, 2025
@openshift-ci-robot
Copy link

/retest-required

Remaining retests: 0 against base HEAD ee06461 and 2 for PR HEAD 32b7839 in total

@bharath-b-rh
Copy link
Contributor

/retest

@openshift-ci-robot
Copy link

/retest-required

Remaining retests: 0 against base HEAD d346a70 and 1 for PR HEAD 32b7839 in total

@chiragkyal
Copy link
Member Author

/retest

@chiragkyal
Copy link
Member Author

/test all

@openshift-ci openshift-ci bot removed the lgtm Indicates that a PR is ready to be merged. label Oct 22, 2025
Copy link
Contributor

openshift-ci bot commented Oct 22, 2025

New changes are detected. LGTM label has been removed.

Copy link
Contributor

openshift-ci bot commented Oct 22, 2025

@chiragkyal: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/unit 32b7839 link true /test unit

Full PR test history. Your PR dashboard.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
pkg/controller/istiocsr/test_utils.go (2)

3-11: Add missing math/big import for certificate serials.

Required to set x509.Certificate.SerialNumber. This was flagged earlier; appears regressed.

Apply:

 import (
   "context"
   "crypto/rand"
   "crypto/rsa"
   "crypto/x509"
   "crypto/x509/pkix"
   "encoding/pem"
+  "math/big"
   "fmt"
   "testing"
   "time"
 )

313-327: Set a random SerialNumber before CreateCertificate.

x509.CreateCertificate returns an error when SerialNumber is nil.

Apply:

   // Apply tweaks to modify the template
   tweak(template)

+  // Set a random serial number (128-bit)
+  serialLimit := new(big.Int).Lsh(big.NewInt(1), 128)
+  serial, err := rand.Int(rand.Reader, serialLimit)
+  if err != nil {
+    panic(fmt.Sprintf("Failed to generate serial number: %v", err))
+  }
+  template.SerialNumber = serial
🧹 Nitpick comments (6)
pkg/controller/istiocsr/test_utils.go (2)

313-315: Defensive: allow nil tweak.

Prevents a panic if callers pass nil.

Apply:

-  // Apply tweaks to modify the template
-  tweak(template)
+  // Apply tweaks to modify the template
+  if tweak != nil {
+    tweak(template)
+  }

1-41: Do not ship test utilities in production package.

This file (non-_test.go) imports testing and exposes test-only helpers, adding dead code to the operator binary and coupling e2e to controller internals. Move helpers to a test-only package.

Recommended:

  • Rename to pkg/controller/istiocsr/test_utils_test.go (or add //go:build test).
  • Move GenerateCertificate and CertificateTweak to test/library (so e2e can import from test/library instead of controller package).
  • Keep constants (IstiocsrCAConfigMapName/IstiocsrCAKeyName) in production constants.go and import those in e2e.

If you want, I can draft a follow-up PR plan to relocate these without breaking e2e.

pkg/controller/istiocsr/controller_test.go (1)

47-53: Reduce duplication in test stubs.

The GetCalls branches for Issuer/Secret and ExistsCalls for ConfigMap repeat across cases. Extract small helpers (e.g., setupIssuerAndSecret(m), setupDeploymentAndCAConfigMap(m)) to keep tests terse.

Also applies to: 133-139, 229-235, 61-64, 157-160

test/e2e/testdata/istio/istiocsr_with_ca_certificate_template.yaml (1)

14-18: Quote templated values; keep conditional on its own lines.

Improves robustness with linters/rendering.

Apply:

-        name: {{.ConfigMapName}}
-{{- if .CustomNamespace}}
-        namespace: {{.CustomNamespace}}
-{{- end}}
-        key: {{.ConfigMapKey}}
+        name: "{{ .ConfigMapName }}"
+{{- if .CustomNamespace }}
+        namespace: "{{ .CustomNamespace }}"
+{{- end }}
+        key: "{{ .ConfigMapKey }}"
test/e2e/istio_csr_test.go (2)

17-17: Decouple e2e from controller’s test helpers.

e2e imports pkg/controller/istiocsr for GenerateCertificate and constants. Prefer:

  • Keep constants in production constants.go (import from istiocsr).
  • Move GenerateCertificate into test/library and import from there in e2e.

This avoids shipping test helpers in prod packages and removes tight coupling with controller internals.

Also applies to: 624-879


519-622: Minor: factor CA ConfigMap verifiers.

The three helpers are great; consider moving them to test/library for reuse by other e2e contexts.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 32b7839 and ddf7e7b.

📒 Files selected for processing (9)
  • pkg/controller/istiocsr/constants.go (1 hunks)
  • pkg/controller/istiocsr/controller.go (3 hunks)
  • pkg/controller/istiocsr/controller_test.go (9 hunks)
  • pkg/controller/istiocsr/deployments.go (10 hunks)
  • pkg/controller/istiocsr/deployments_test.go (13 hunks)
  • pkg/controller/istiocsr/install_instiocsr_test.go (2 hunks)
  • pkg/controller/istiocsr/test_utils.go (3 hunks)
  • test/e2e/istio_csr_test.go (5 hunks)
  • test/e2e/testdata/istio/istiocsr_with_ca_certificate_template.yaml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/controller/istiocsr/controller.go
  • pkg/controller/istiocsr/constants.go
🧰 Additional context used
🪛 YAMLlint (1.37.1)
test/e2e/testdata/istio/istiocsr_with_ca_certificate_template.yaml

[error] 16-16: syntax error: could not find expected ':'

(syntax)

🔇 Additional comments (16)
pkg/controller/istiocsr/install_instiocsr_test.go (1)

43-53: LGTM: prereq Get hooks for Issuer and Secret.

Pre-populating Issuer and Secret via GetCalls is correct and keeps Create path focused on label checks.

test/e2e/istio_csr_test.go (1)

191-194: LGTM: DeferCleanup for Job teardown.

Proper cleanup via background propagation prevents leaked Jobs/pods between specs.

Also applies to: 268-271, 338-341

pkg/controller/istiocsr/deployments_test.go (4)

6-6: LGTM!

The reflect import is appropriately used for deep equality checks when comparing volumes and volume mounts in the new TestUpdateVolumeWithIssuerCA test function.


36-58: LGTM!

The test scaffolding correctly mocks the ConfigMap, Issuer, and Secret interactions required by the new CA certificate handling flow. The pattern is consistently applied across test cases.


790-1064: Excellent test coverage for CA certificate handling!

The test cases comprehensively cover:

  • Success paths (default and custom namespace)
  • Validation failures (missing ConfigMap, missing key, invalid PEM, non-CA certificate, missing KeyUsageCertSign)
  • Infrastructure failures (watch label update)

The test setup correctly simulates each scenario and validates appropriate error messages.


1199-1314: LGTM! Well-structured test for volume mounting logic.

The test function thoroughly validates that updateVolumeWithIssuerCA correctly:

  • Adds CA volumes when none exist
  • Preserves existing volumes while adding CA volumes
  • Updates existing CA volumes while preserving others

The use of reflect.DeepEqual is appropriate for comparing complex nested volume and mount structures.

pkg/controller/istiocsr/deployments.go (10)

4-5: LGTM!

The new imports are necessary and appropriately used:

  • crypto/x509 and encoding/pem for PEM certificate validation in validatePEMData
  • types for types.NamespacedName in handleUserProvidedCA

Also applies to: 16-16


266-282: LGTM! Clear separation of CA configuration paths.

The refactored updateVolumes function cleanly separates user-provided CA certificate handling from issuer-based CA certificate handling, with appropriate early return when user configuration is present.


284-351: Excellent implementation of user-provided CA handling!

The function is well-structured with:

  • Clear step-by-step processing with helpful comments
  • Proper namespace defaulting logic
  • Watch label addition before validation (ensures fixes trigger reconciliation)
  • Comprehensive validation (key existence, PEM format)
  • Detailed comment explaining the design rationale for creating a managed copy
  • Appropriate error handling (NewIrrecoverableError for validation, FromClientError for client ops)

353-393: LGTM! Clean separation of issuer-based CA paths.

The function appropriately handles two scenarios:

  • CA issuer with secret reference
  • Non-CA issuer (fetches from istiod certificate)

The shouldUpdateVolume flag pattern ensures the volume is only updated when CA data is available.


395-464: LGTM! Improved volume mounting logic.

The refactored function now correctly:

  • Uses exported constants (IstiocsrCAConfigMapName, IstiocsrCAKeyName)
  • Updates existing volumes/mounts rather than replacing all volumes
  • Preserves other volumes and mounts in the deployment

This is a significant improvement over wholesale replacement and aligns with the test expectations.


487-507: LGTM! Well-structured function for istiod certificate-based CA.

The function correctly:

  • Fetches the certificate object
  • Retrieves the secret referenced in the certificate spec
  • Adds a watch label for change tracking
  • Extracts CA data using the standard key constant
  • Delegates ConfigMap creation/update to the helper function

509-528: LGTM! Better naming and consistent pattern.

The renamed function (createCAConfigMapFromIssuerSecret) is more descriptive and follows the same pattern as createCAConfigMapFromIstiodCertificate, improving code consistency.


530-575: LGTM! Well-structured ConfigMap management.

The createOrUpdateCAConfigMap function consolidates the ConfigMap creation/update logic and follows the standard operator reconciliation pattern:

  • Check existence
  • Update if changed
  • Create if missing
  • Record appropriate events

This reduces code duplication and ensures consistent behavior across different CA sources.


577-611: Excellent PEM validation implementation!

The validatePEMData function provides comprehensive validation:

  • PEM structure and decoding
  • Certificate type verification
  • Basic Constraints validation
  • IsCA flag check
  • KeyUsageCertSign verification

The validation is thorough and error messages are clear, which will help users quickly identify and fix configuration issues.


613-626: LGTM! Good generalization of watch label logic.

The function is now properly generalized to accept any client.Object, allowing it to be reused for ConfigMaps, Secrets, and other Kubernetes resources. The implementation correctly handles labels and persists changes.

Note: The error message at line 623 may contain a double space when obj.GetName() returns an empty string, but this is intentional and accurately reflects the actual error format (as confirmed in past review discussions).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. docs-approved Signifies that Docs has signed off on this PR jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. px-approved Signifies that Product Support has signed off on this PR qe-approved Signifies that QE has signed off on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants