Skip to content

Conversation

@alphaprinz
Copy link
Contributor

@alphaprinz alphaprinz commented Nov 11, 2025

Explain the changes

For a cluster with cnpg pg, add POSTGRES_HOST_RO to core's env.
Unlike other params used by pg client to connect, RO host is not part of cnpg's app secret, so we need to some more work to set it.
Core pr: noobaa/noobaa-core#9237

Issues: Fixed #xxx / Gap #xxx

Testing Instructions:

  • Doc added/updated
  • Tests added

Summary by CodeRabbit

  • New Features
    • Added read-only PostgreSQL host environment configuration to core container settings.
    • Automatically discovers and configures read-only PostgreSQL replica hosts for CloudNative PostgreSQL deployments.
    • Service discovery now identifies replica instances and registers them as read-only endpoints.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 11, 2025

Walkthrough

Adds a new POSTGRES_HOST_RO environment variable to the core StatefulSet manifest, updates the embedded bundle checksum, and adds logic to set POSTGRES_HOST_RO to the CNPG cluster name suffixed with "-ro" when reconciling CNPG-managed Postgres.

Changes

Cohort / File(s) Summary
StatefulSet manifest & bundle
deploy/internal/statefulset-core.yaml, pkg/bundle/deploy.go
Inserted POSTGRES_HOST_RO env var into the core container spec and updated the embedded bundle SHA constant to reflect the manifest change.
CNPG reconciliation logic
pkg/system/phase2_creating.go
In setDesiredCoreEnv, added handling to populate POSTGRES_HOST_RO when shouldReconcileCNPGCluster() is true by deriving the read-only host from the CNPG cluster name plus suffix -ro. No public API/signature changes.

Sequence Diagram

sequenceDiagram
    participant Reconciler as Reconciler\n(phase2_creating)
    participant CNPG as CNPGCluster object
    participant Core as Desired Core StatefulSet Env

    rect rgb(200,220,255)
    Note over Reconciler: Gate - only if shouldReconcileCNPGCluster()
    Reconciler->>Reconciler: shouldReconcileCNPGCluster() == true
    end

    rect rgb(220,240,220)
    Note over Reconciler,CNPG: Derive RO host
    Reconciler->>CNPG: Read CNPGCluster.Name
    CNPG-->>Reconciler: returns <cluster-name>
    Reconciler->>Reconciler: compute roHost = <cluster-name> + "-ro"
    end

    rect rgb(240,240,200)
    Note over Reconciler,Core: Configure env
    Reconciler->>Core: Set env POSTGRES_HOST_RO = roHost
    Core-->>Reconciler: Desired spec updated
    end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Review focus:
    • pkg/system/phase2_creating.go — correctness of the condition, CNPG name read, string concat, and nil/error handling.
    • deploy/internal/statefulset-core.yaml / pkg/bundle/deploy.go — verify env var insertion and SHA update.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title accurately summarizes the main change: adding POSTGRES_HOST_RO to the core container when using a CNPG cluster, which aligns with the file modifications across the deployment manifest, generated code, and reconciliation logic.
Description check ✅ Passed The pull request description covers the main change and provides context about why POSTGRES_HOST_RO needs special handling. However, it is incomplete: Testing Instructions and the Issues section contain only placeholders, and the Doc/Tests checklist items are unchecked with no explanation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb69db2 and 78fc9a8.

📒 Files selected for processing (1)
  • pkg/system/phase2_creating.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/system/phase2_creating.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
  • GitHub Check: run-kms-key-rotate-test
  • GitHub Check: run-operator-tests
  • GitHub Check: run-kms-tls-token-test
  • GitHub Check: run-azure-vault-test
  • GitHub Check: run-cli-tests
  • GitHub Check: run-kms-dev-test
  • GitHub Check: run-kms-kmip-test
  • GitHub Check: run-admission-test
  • GitHub Check: run-kms-tls-sa-test
  • GitHub Check: golangci-lint
  • GitHub Check: run-core-config-map-tests
  • GitHub Check: cnpg-deployment-test
  • GitHub Check: run-hac-test

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


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: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aee069d and a103e16.

📒 Files selected for processing (3)
  • deploy/internal/statefulset-core.yaml (1 hunks)
  • pkg/bundle/deploy.go (2 hunks)
  • pkg/system/phase2_creating.go (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-02T04:34:47.006Z
Learnt from: naveenpaul1
Repo: noobaa/noobaa-operator PR: 1607
File: pkg/bundle/deploy.go:0-0
Timestamp: 2025-07-02T04:34:47.006Z
Learning: Default values for `METRICS_AUTH_ENABLED` and `VERSION_AUTH_ENABLED` are set up under `noobaa-config` in the NooBaa operator.

Applied to files:

  • deploy/internal/statefulset-core.yaml
  • pkg/bundle/deploy.go
🧬 Code graph analysis (1)
pkg/system/phase2_creating.go (1)
pkg/util/util.go (1)
  • KubeList (646-667)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
  • GitHub Check: run-admission-test
  • GitHub Check: run-operator-tests
  • GitHub Check: cnpg-deployment-test
  • GitHub Check: run-kms-tls-sa-test
  • GitHub Check: run-kms-dev-test
  • GitHub Check: run-kms-tls-token-test
  • GitHub Check: run-azure-vault-test
  • GitHub Check: golangci-lint
  • GitHub Check: run-hac-test
  • GitHub Check: run-cli-tests
  • GitHub Check: run-core-config-map-tests
  • GitHub Check: run-kms-kmip-test
  • GitHub Check: run-kms-key-rotate-test
🔇 Additional comments (3)
deploy/internal/statefulset-core.yaml (1)

122-122: LGTM! Environment variable correctly added.

The POSTGRES_HOST_RO environment variable is properly added to the core container's environment. The empty value is intentional, as it will be dynamically populated during reconciliation in pkg/system/phase2_creating.go.

pkg/bundle/deploy.go (2)

5296-5296: SHA bump looks consistent with the YAML change.

Sha256_deploy_internal_statefulset_core_yaml updated; matches the added env below. No action required.


5419-5420: Implementation correctly validates reconciliation and fallback—no issues found.

The codebase confirms all review concerns:

  1. Phase2 reconciliation for POSTGRES_HOST_RO: phase2_creating.go:455-464 conditionally populates this env only when shouldReconcileCNPGCluster() is true, correctly querying services with label selector app=noobaa,cnpg.io/cluster=noobaa-db-pg-cluster and selecting the replica service by cnpg.io/instanceRole == "replica" selector.

  2. Core fallback tolerance: When CNPG is not enabled, the conditional block is skipped entirely, leaving POSTGRES_HOST_RO unset by design. This implicit tolerance is working as intended.

  3. Architecture documentation: doc/noobaa-cnpg-integration.md documents that read-only and read-write services are automatically created and managed by CNPG, confirming the integration design is sound.

The optional e2e/unit test suggestion is a quality-of-life improvement but not required—the current implementation correctly handles both enabled and disabled CNPG scenarios.

Comment on lines 455 to 467
case "POSTGRES_HOST_RO":
if r.shouldReconcileCNPGCluster() {
//RO host is not part of the cnpg app secret, so we're getting it here from the service
services := &corev1.ServiceList{}
cnpg_label, _ := labels.Parse("app=noobaa,cnpg.io/cluster=noobaa-db-pg-cluster")
util.KubeList(services, &client.ListOptions{Namespace: r.NooBaaPostgresDB.Namespace, LabelSelector: cnpg_label})
for _, service := range services.Items {
if (service.Spec.Selector["cnpg.io/instanceRole"] == "replica") {
c.Env[j].Value = service.Name
break
}
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix critical error handling and service discovery issues.

This code has several critical issues that could cause runtime failures or silent misconfigurations:

  1. Line 460: Error from labels.Parse is silently ignored. If parsing fails, cnpg_label will be invalid, leading to incorrect service queries.

  2. Line 460: Return value of util.KubeList is not checked. Per the function signature in pkg/util/util.go:645-666, it returns false when the list operation fails.

  3. Line 460: The cluster name is hardcoded as "noobaa-db-pg-cluster" instead of using r.CNPGCluster.Name (which is used elsewhere, e.g., line 637). This will break if the actual cluster has a different name.

  4. Lines 461-466: If no service with cnpg.io/instanceRole == "replica" is found, the environment variable remains unset. This should be logged and potentially treated as an error since RO host functionality would be broken.

  5. Line 462: service.Spec.Selector could be nil, leading to a panic. Add a nil check.

Apply this diff to fix the issues:

 case "POSTGRES_HOST_RO":
 	if r.shouldReconcileCNPGCluster() {
-		//RO host is not part of the cnpg app secret, so we're getting it here from the service
+		// RO host is not part of the cnpg app secret, so we're getting it here from the service
 		services := &corev1.ServiceList{}
-		cnpg_label, _ := labels.Parse("app=noobaa,cnpg.io/cluster=noobaa-db-pg-cluster")
-		util.KubeList(services, &client.ListOptions{Namespace: r.NooBaaPostgresDB.Namespace, LabelSelector: cnpg_label})
+		cnpgLabel, err := labels.Parse(fmt.Sprintf("app=noobaa,cnpg.io/cluster=%s", r.CNPGCluster.Name))
+		if err != nil {
+			r.Logger.Errorf("Failed to parse CNPG label selector: %v", err)
+			break
+		}
+		if !util.KubeList(services, &client.ListOptions{Namespace: r.NooBaaPostgresDB.Namespace, LabelSelector: cnpgLabel}) {
+			r.Logger.Errorf("Failed to list services for CNPG cluster %s", r.CNPGCluster.Name)
+			break
+		}
+		found := false
 		for _, service := range services.Items {
-			if (service.Spec.Selector["cnpg.io/instanceRole"] == "replica") {
+			if service.Spec.Selector != nil && service.Spec.Selector["cnpg.io/instanceRole"] == "replica" {
 				c.Env[j].Value = service.Name
+				r.Logger.Infof("Set POSTGRES_HOST_RO to %s", service.Name)
+				found = true
 				break
 			}
 		}
+		if !found {
+			r.Logger.Warnf("No replica service found for CNPG cluster %s, POSTGRES_HOST_RO will not be set", r.CNPGCluster.Name)
+		}
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case "POSTGRES_HOST_RO":
if r.shouldReconcileCNPGCluster() {
//RO host is not part of the cnpg app secret, so we're getting it here from the service
services := &corev1.ServiceList{}
cnpg_label, _ := labels.Parse("app=noobaa,cnpg.io/cluster=noobaa-db-pg-cluster")
util.KubeList(services, &client.ListOptions{Namespace: r.NooBaaPostgresDB.Namespace, LabelSelector: cnpg_label})
for _, service := range services.Items {
if (service.Spec.Selector["cnpg.io/instanceRole"] == "replica") {
c.Env[j].Value = service.Name
break
}
}
}
case "POSTGRES_HOST_RO":
if r.shouldReconcileCNPGCluster() {
// RO host is not part of the cnpg app secret, so we're getting it here from the service
services := &corev1.ServiceList{}
cnpgLabel, err := labels.Parse(fmt.Sprintf("app=noobaa,cnpg.io/cluster=%s", r.CNPGCluster.Name))
if err != nil {
r.Logger.Errorf("Failed to parse CNPG label selector: %v", err)
break
}
if !util.KubeList(services, &client.ListOptions{Namespace: r.NooBaaPostgresDB.Namespace, LabelSelector: cnpgLabel}) {
r.Logger.Errorf("Failed to list services for CNPG cluster %s", r.CNPGCluster.Name)
break
}
found := false
for _, service := range services.Items {
if service.Spec.Selector != nil && service.Spec.Selector["cnpg.io/instanceRole"] == "replica" {
c.Env[j].Value = service.Name
r.Logger.Infof("Set POSTGRES_HOST_RO to %s", service.Name)
found = true
break
}
}
if !found {
r.Logger.Warnf("No replica service found for CNPG cluster %s, POSTGRES_HOST_RO will not be set", r.CNPGCluster.Name)
}
}
🤖 Prompt for AI Agents
In pkg/system/phase2_creating.go around lines 455-467, the service discovery
block must validate inputs and handle errors: parse the label expression and
check the error returned by labels.Parse, use the actual cluster name
(r.CNPGCluster.Name) instead of the hardcoded "noobaa-db-pg-cluster" when
building the label, call util.KubeList and check its boolean return (handle/log
and return on failure), nil-check service.Spec.Selector before indexing it to
avoid panics, and after iterating if no replica service was found log an error
(or set an appropriate error/condition) so the POSTGRES_HOST_RO env var is not
silently left unset. Ensure all failures are logged with context and return or
propagate an error so the caller can react.

Copy link
Member

@dannyzaken dannyzaken left a comment

Choose a reason for hiding this comment

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

My previous comments aside, I think that, on the operator side, it should be ok to assume the service name will be r.CNPGCluster.Name+"-ro". You can just put this as the value

@alphaprinz alphaprinz merged commit ef270ff into noobaa:master Nov 24, 2025
16 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants