Skip to content

OLS-3799 Add wait-for-rhokp init container to app-server deployment - #1921

Open
vimalk78 wants to merge 1 commit into
openshift:mainfrom
vimalk78:ols-3799
Open

OLS-3799 Add wait-for-rhokp init container to app-server deployment#1921
vimalk78 wants to merge 1 commit into
openshift:mainfrom
vimalk78:ols-3799

Conversation

@vimalk78

@vimalk78 vimalk78 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds a wait-for-rhokp init container to the app-server Deployment when ByokRAGOnly=false
  • Polls the RHOKP/Solr HTTPS endpoint with curl using exponential backoff (360s timeout, matching RHOKP's startup probe budget)
  • Prevents the app-server from starting before Solr is reachable, fixing a race condition where SolrHybridSearch's @cached_property permanently caches None if RHOKP takes longer than the 120s retry window
  • Follows the existing wait-for-postgres init container pattern

Test plan

  • Unit tests for GenerateRHOKPWaitInitContainer (image, security context, CA mount, curl command, timeout, namespace)
  • Deployment tests: init container present when ByokRAGOnly=false, absent when ByokRAGOnly=true
  • Existing assets_test.go ConsistOf assertion updated for the new init container
  • make test passes (130/130 specs)
  • E2e: deploy with ByokRAGOnly=false, verify init container blocks app-server startup until RHOKP is serving

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added RHOKP/Solr readiness checks during RAG deployment startup.
    • Securely verifies RHOKP availability using mounted certificates and bounded retry behavior.
    • BYOK-only deployments skip the RHOKP readiness step.
  • Bug Fixes

    • Prevents RAG components from starting before RHOKP is available.
    • Improves deployment reliability when RHOKP requires additional time to become ready.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 43545d3d-6561-407d-8970-70279ad91596

📥 Commits

Reviewing files that changed from the base of the PR and between 0178cf7 and 3b1c6ae.

📒 Files selected for processing (6)
  • internal/controller/appserver/assets_test.go
  • internal/controller/appserver/deployment.go
  • internal/controller/appserver/deployment_test.go
  • internal/controller/utils/constants.go
  • internal/controller/utils/rhokp_wait.go
  • internal/controller/utils/rhokp_wait_test.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • internal/controller/utils/constants.go
  • internal/controller/appserver/assets_test.go
  • internal/controller/appserver/deployment_test.go
  • internal/controller/appserver/deployment.go
  • internal/controller/utils/rhokp_wait.go

📝 Walkthrough

Walkthrough

The PR adds a RHOKP/Solr readiness init container. Non-BYOK-only deployments include it. BYOK-only deployments omit it. The container polls HTTPS with a mounted CA certificate, bounded backoff, restricted security settings, and a 360-second timeout.

Changes

RHOKP readiness integration

Layer / File(s) Summary
RHOKP wait-container implementation
internal/controller/utils/constants.go, internal/controller/utils/rhokp_wait.go, internal/controller/utils/rhokp_wait_test.go
Adds the wait-for-rhokp name, the GenerateRHOKPWaitInitContainer utility, HTTPS readiness polling, bounded backoff, security settings, resource limits, CA mounting, and tests.
Conditional deployment wiring
internal/controller/appserver/deployment.go, internal/controller/appserver/deployment_test.go, internal/controller/appserver/assets_test.go
Adds the wait container when ByokRAGOnly is false and verifies its absence for BYOK-only deployments.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GenerateOLSDeployment
  participant GenerateRHOKPWaitInitContainer
  participant RHOKPSolr
  GenerateOLSDeployment->>GenerateRHOKPWaitInitContainer: Generate wait-for-rhokp when ByokRAGOnly is false
  GenerateRHOKPWaitInitContainer-->>GenerateOLSDeployment: Return configured init container
  GenerateRHOKPWaitInitContainer->>RHOKPSolr: Poll HTTPS admin/ping endpoint with CA certificate
  RHOKPSolr-->>GenerateRHOKPWaitInitContainer: Readiness response or timeout
Loading

Possibly related PRs

Suggested reviewers: joshuawilson, raptorsun

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the addition of the wait-for-rhokp init container to the app-server deployment.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@blublinsky blublinsky left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Must-fix: cr.Namespace is always empty for the cluster-scoped OLSConfig CRD

At deployment.go:447:

initContainers = append(initContainers, utils.GenerateRHOKPWaitInitContainer(r.GetAppServerImage(), cr.Namespace))

OLSConfig is scope: Cluster, so cr.Namespace is always "". This produces a URL like https://lightspeed-rhokp..svc:8443/... (double dots — invalid DNS).

Suggested fix: Drop the namespace parameter entirely and use the short service name, matching the Postgres wait pattern. Since the init container runs in the same namespace as the RHOKP service, Kubernetes DNS resolves the short name automatically:

rhokpURL := fmt.Sprintf("https://%s:%d/solr/portal-rag/select?q=*:*&rows=0&wt=json",
    RHOKPServiceName, RHOOKPImageHTTPSPort)

This also simplifies GenerateRHOKPWaitInitContainer — it no longer needs the namespace argument, just like GeneratePostgresWaitInitContainer only takes the image.

Note: --cacert TLS verification with a short hostname may require the service-ca cert to include the short name as a SAN. If FQDN is needed for TLS cert matching, use the constant utils.OLSNamespaceDefault instead of cr.Namespace.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@blublinsky blublinsky left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Questioning the approach: should this fix live in the operator at all?

The app-server (lightspeed-service-api) already has retry logic for Solr connectivity. The root cause described here is that SolrHybridSearch uses @cached_property which permanently caches None on failure — that's a bug in the service code, not a deployment ordering problem.

The proper fix is in lightspeed-service-api: don't permanently cache a transient failure. For example, use a lazy property that retries on None, or replace @cached_property with a retry-aware initializer. This makes the service resilient regardless of startup ordering, container restarts, or network blips — situations an init container cannot help with anyway.

Adding an init container with shell scripts, CA mounts, and backoff logic in the operator is treating the symptom at the wrong layer. Compare with the Postgres wait: that exists because the app literally cannot start without a DB connection. Solr/RHOKP is not a hard prerequisite — the app should degrade gracefully and recover when it becomes available.

I'd suggest fixing the caching bug in the service and closing this PR. Happy to discuss if there's additional context I'm missing.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/controller/utils/rhokp_wait_test.go`:
- Around line 19-32: Strengthen the test for GenerateRHOKPWaitInitContainer to
validate the complete readiness command and its behavior, not just command
length or marker text. Control curl, date, and sleep so the test covers the
successful probe, timeout exit status, bounded backoff, and the 360-second
RHOKPWaitMaxSeconds boundary; ensure the generated command does not perform a
probe or sleep after the deadline. Assert the full command contents, including
the complete Solr query and expected exit statuses, while preserving the
existing container-spec checks.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 308fa67e-bdc1-408a-9c49-fc544800ad31

📥 Commits

Reviewing files that changed from the base of the PR and between 0178cf7 and 801fa20.

📒 Files selected for processing (6)
  • internal/controller/appserver/assets_test.go
  • internal/controller/appserver/deployment.go
  • internal/controller/appserver/deployment_test.go
  • internal/controller/utils/constants.go
  • internal/controller/utils/rhokp_wait.go
  • internal/controller/utils/rhokp_wait_test.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • internal/controller/utils/constants.go
  • internal/controller/appserver/assets_test.go
  • internal/controller/appserver/deployment_test.go
  • internal/controller/appserver/deployment.go
  • internal/controller/utils/rhokp_wait.go

Comment thread internal/controller/utils/rhokp_wait_test.go

@vimalk78 vimalk78 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Adversarial Review

Overall the approach is sound — gating on !ByokRAGOnly is consistent, the timeout matches the RHOKP startup probe budget, and the exponential backoff is well-implemented. Four issues found in inline comments below.

[ "$sleep_sec" -gt "$max_sleep" ] && sleep_sec="$max_sleep"
}

while true; do

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Missing command -v curl guard

postgres_wait.go validates its tool exists before entering the poll loop:

if ! command -v pg_isready >/dev/null 2>&1; then
  echo "wait-for-postgres: pg_isready not found in image" >&2
  exit 1
fi

This script has no equivalent check for curl. If curl is ever removed from the app-server image, the init container will loop for 360 seconds (the curl invocation suppresses all output via >/dev/null 2>&1) before timing out — a confusing silent failure instead of a fast one.

Suggestion: add command -v curl check before the while true loop, matching the postgres pattern.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added command -v curl guard before the poll loop, matching the postgres pattern. Fails fast with a clear message instead of silently looping for 360s.

Comment thread internal/controller/utils/rhokp_wait.go Outdated
},
SecurityContext: &corev1.SecurityContext{
AllowPrivilegeEscalation: &[]bool{false}[0],
ReadOnlyRootFilesystem: &[]bool{true}[0],

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Incomplete SecurityContext — missing PSA "restricted" fields

This only sets AllowPrivilegeEscalation and ReadOnlyRootFilesystem. The codebase has RestrictedContainerSecurityContext() (utils/utils.go:73) that also sets:

  • RunAsNonRoot: true
  • SeccompProfile: RuntimeDefault
  • Capabilities: Drop ALL

These are required by the Pod Security "restricted" profile. Without them, the pod could fail admission on namespaces with PSA enforcement.

Note: wait-for-postgres has the same gap (pre-existing), but this PR should not compound it.

Suggestion: use RestrictedContainerSecurityContext() instead of the inline SecurityContext.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Switched to RestrictedContainerSecurityContext() — picks up RunAsNonRoot, SeccompProfile RuntimeDefault, and Drop ALL capabilities.

Comment thread internal/controller/utils/rhokp_wait.go Outdated
// lightweight Solr select query. The RHOKP CA certificate volume must already
// be defined on the pod (AppRHOKPCACertVolumeName).
func GenerateRHOKPWaitInitContainer(image, namespace string) corev1.Container {
rhokpURL := fmt.Sprintf("https://%s.%s.svc:%d/solr/portal-rag/select?q=*:*&rows=0&wt=json",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Readiness endpoint mismatch + duplicated URL construction

Two issues on this line:

  1. Endpoint: This polls /solr/portal-rag/select?q=*:*&rows=0&wt=json, but RHOKP's own readiness/startup probes use RHOOKPReadinessHTTPPath = /solr/portal-rag/admin/ping. The select endpoint is heavier — it queries the Solr index (even with rows=0). During startup, /admin/ping becomes available first. The init container should use the same endpoint as the readiness probe.

  2. URL helper: There's an existing RHOKPServiceURL(namespace) helper (constants.go:714) that returns https://lightspeed-rhokp.<namespace>.svc:8443. This could be reused:

rhokpURL := RHOKPServiceURL(namespace) + RHOOKPReadinessHTTPPath

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed — now uses RHOKPServiceURL(namespace) + RHOOKPReadinessHTTPPath (/solr/portal-rag/admin/ping). Lighter endpoint, available earlier during startup, and no duplicated URL construction.

When ByokRAGOnly=false the operator now adds a wait-for-rhokp init
container that polls RHOKP/Solr until it responds, preventing the
app-server from starting before Solr is reachable.  Without this gate
the SolrHybridSearch @cached_property can permanently cache None if
RHOKP takes longer than the 120 s retry window.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Vimal Kumar <vimal78@gmail.com>
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@vimalk78 vimalk78 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed — switched from cr.Namespace to r.GetNamespace() which returns the operator watch namespace. Kept the FQDN approach (with namespace) rather than short service name because the service-ca TLS cert SANs use the FQDN — curl --cacert would fail cert validation against a short hostname.

@vimalk78

vimalk78 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

Re @blublinsky review — fixed. Switched from cr.Namespace (always empty for cluster-scoped OLSConfig) to r.GetNamespace() which returns the operator watch namespace. Kept the FQDN rather than short service name because service-ca TLS certs use the FQDN as SAN — curl --cacert would fail cert validation against a short hostname.

@raptorsun

Copy link
Copy Markdown
Contributor

/lgtm
/approve

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Aug 5, 2026
@raptorsun

Copy link
Copy Markdown
Contributor

the fix to lightspeed service as @blublinsky suggested can be addressed in another PR to improve resiliancy of that component.

@openshift-ci

openshift-ci Bot commented Aug 5, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: raptorsun

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

The pull request process is described here

Details 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 Aug 5, 2026
@openshift-ci

openshift-ci Bot commented Aug 5, 2026

Copy link
Copy Markdown

@vimalk78: all tests passed!

Full PR test history. Your PR dashboard.

Details

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.

@raptorsun

Copy link
Copy Markdown
Contributor

/hold
pending decision over 2 solutions: this one and the service fix

@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 Aug 5, 2026
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. do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. lgtm Indicates that a PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants