Skip to content

RHIDP-14790: Add OLM v1 code path to install-rhdh-catalog-source.sh#3047

Open
Fortune-Ndlovu wants to merge 2 commits into
redhat-developer:mainfrom
Fortune-Ndlovu:RHIDP-14790-operator-add-olm-v-1-code-path-to-install-rhdh-catalog-source-sh
Open

RHIDP-14790: Add OLM v1 code path to install-rhdh-catalog-source.sh#3047
Fortune-Ndlovu wants to merge 2 commits into
redhat-developer:mainfrom
Fortune-Ndlovu:RHIDP-14790-operator-add-olm-v-1-code-path-to-install-rhdh-catalog-source-sh

Conversation

@Fortune-Ndlovu

@Fortune-Ndlovu Fortune-Ndlovu commented Jun 23, 2026

Copy link
Copy Markdown
Member

Adds an --olm-version v0|v1|auto CLI flag (default: auto) to install-rhdh-catalog-source.sh with CRD-based OLM v1 detection. When OLM v1 is detected (or forced), the script creates a ClusterCatalog and ClusterExtension with a ServiceAccount and ClusterRoleBinding, instead of the OLM v0 CatalogSource, Subscription, and OperatorGroup. Resolution is pinned to the custom catalog via selector.matchLabels, and CRD upgrade safety preflight is disabled (enforcement: None) per the known blocker RHIDP-8656. The OLM v0 path is fully preserved for backward compatibility on older clusters.

Which issue(s) does this PR fix or relate to

Resolves: https://redhat.atlassian.net/browse/RHIDP-14790

PR acceptance criteria

  • Tests
  • Documentation

How to test changes / Special notes to the reviewer

bash .rhdh/scripts/install-rhdh-catalog-source.sh --latest --install-operator rhdh 2>&1 | tee /tmp/olmv1-test3.log)

Signed-off-by: Fortune-Ndlovu fndlovu@redhat.com
Assisted-by: Claude (claude-opus-4-6)

Add --olm-version v0|v1|auto flag (default: auto) with CRD-based
detection of OLM v1. When OLM v1 is detected, creates ClusterCatalog,
ServiceAccount, ClusterRoleBinding, and ClusterExtension instead of
OLM v0 CatalogSource, OperatorGroup, and Subscription. The IIB
rendering/rebuild phase is shared by both paths. OLM v0 behavior
is fully preserved for backward compatibility.

Signed-off-by: Fortune-Ndlovu <fndlovu@redhat.com>
Assisted-by: Claude (claude-opus-4-6)
@rhdh-qodo-merge

Copy link
Copy Markdown

PR Summary by Qodo

Add OLM v1 support to install-rhdh-catalog-source.sh
✨ Enhancement 🕐 40+ Minutes

Grey Divider

Description

• Add --olm-version v0|v1|auto with CRD-based auto-detection on OpenShift.
• When OLM v1 is available, install via ClusterCatalog + ClusterExtension and installer RBAC.
• Preserve OLM v0 CatalogSource/OperatorGroup/Subscription flow for older clusters and Kubernetes.
Diagram

graph TD
A["install-rhdh-catalog-source.sh"] --> B{"Resolve OLM version"}
B -->|"v1"| C["Apply ClusterCatalog"] --> D["Create SA + CRB"] --> E["Apply ClusterExtension"]
B -->|"v0"| F["Apply CatalogSource"] --> G["Apply OperatorGroup"] --> H["Apply Subscription"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Detect OLM v1 via API discovery instead of CRD existence
  • ➕ More robust against CRD name/version changes and partial installs
  • ➕ Can validate required resources (catalogd API group, ClusterCatalog kind) not just ClusterExtension CRD
  • ➖ More code and more cluster calls; harder to keep the script simple
  • ➖ Still needs careful handling across OCP versions and permissions
2. Avoid cluster-admin ClusterRoleBinding by using least-privilege RBAC
  • ➕ Reduces security risk of granting cluster-admin to an installer ServiceAccount
  • ➕ Easier to pass security reviews / safer default behavior
  • ➖ Requires identifying the exact permissions OLM v1 installer needs, which may change
  • ➖ Higher maintenance cost and risk of under-permissioning across versions
3. Split v0 and v1 into separate scripts (thin wrapper chooses which)
  • ➕ Reduces branching complexity and makes each flow easier to reason about
  • ➕ Allows different documentation and guardrails per OLM generation
  • ➖ More files to maintain; duplicated shared steps unless refactored
  • ➖ User experience becomes more fragmented if wrapper behavior isn’t clear

Recommendation: The current approach (single script with --olm-version and CRD-based auto-detection) is a pragmatic way to preserve v0 compatibility while enabling v1. The main strategic concern is the cluster-admin binding for the installer ServiceAccount; if this is unavoidable for now, consider clearly documenting the privilege elevation and, longer-term, tightening RBAC to least privilege. If v1 detection becomes flaky across OCP versions, consider upgrading detection to API discovery of required groups/kinds.

Files changed (1) +244 / -51

Enhancement (1) +244 / -51
install-rhdh-catalog-source.shAdd OLM v1 (ClusterCatalog/ClusterExtension) install path with auto-detection +244/-51

Add OLM v1 (ClusterCatalog/ClusterExtension) install path with auto-detection

• Introduces '--olm-version v0|v1|auto' and resolves the effective OLM version via ClusterExtension CRD presence (OpenShift-only). Adds an OLM v1 code path that creates ClusterCatalog, a pull secret in the catalogd namespace, an installer ServiceAccount + cluster-admin ClusterRoleBinding, and a ClusterExtension pinned to the custom catalog via 'selector.matchLabels'. Keeps the existing OLM v0 CatalogSource/OperatorGroup/Subscription behavior intact and gates OperatorGroup checks to the v0 path.

.rhdh/scripts/install-rhdh-catalog-source.sh

@rhdh-qodo-merge

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📎 Requirement gaps (0) 📜 Skill insights (0)

Context used
✅ Tickets: RHIDP-8656
✅ Compliance rules (platform): 18 rules

Grey Divider


Action required

1. Cluster-admin installer binding 🐞 Bug ⛨ Security
Description
In the OLM v1 path, the script creates a ClusterRoleBinding that grants the installer ServiceAccount
the built-in cluster-admin ClusterRole, giving it full cluster-wide privileges. This leaves a
highly privileged credential in-cluster and substantially increases impact if the ServiceAccount
token is leaked or the namespace is compromised.
Code

.rhdh/scripts/install-rhdh-catalog-source.sh[R937-951]

+  # ClusterRoleBinding granting cluster-admin to the installer SA
+  CRB_NAME="${OPERATOR_NAME_TO_INSTALL}-installer-binding"
+  echo "apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: ${CRB_NAME}
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: cluster-admin
+subjects:
+- kind: ServiceAccount
+  name: ${SA_NAME}
+  namespace: ${NAMESPACE_SUBSCRIPTION}
+" > "$TMPDIR"/ClusterRoleBinding.yml && invoke_cluster_cli apply -f "$TMPDIR"/ClusterRoleBinding.yml
Relevance

⭐⭐ Medium

Repo favors least-privilege RBAC (e.g., removed unused rules in PR #1320/#1374), but no precedent
for installer SA needs.

PR-#1320
PR-#1374

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The OLM v1 branch writes and applies a ClusterRoleBinding manifest that sets `roleRef.name:
cluster-admin` for the installer ServiceAccount, making it cluster-admin cluster-wide.

.rhdh/scripts/install-rhdh-catalog-source.sh[928-951]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The OLM v1 install flow creates a `ClusterRoleBinding` to `cluster-admin` for the `${OPERATOR_NAME_TO_INSTALL}-installer` ServiceAccount. This grants broad, persistent cluster-admin privileges, which is an unnecessary security risk for a script-driven install path.

### Issue Context
The new OLM v1 code path creates a ServiceAccount and binds it to `cluster-admin` before creating the `ClusterExtension`.

### Fix Focus Areas
- Replace the `cluster-admin` ClusterRoleBinding with least-privilege permissions required by the OLM v1 ClusterExtension installer (use an existing purpose-built ClusterRole if available, otherwise define a custom ClusterRole with only needed verbs/resources).
- If elevated privileges are truly required, gate them behind an explicit flag (e.g. `--grant-cluster-admin`) and/or clean them up after installation completes (delete the ClusterRoleBinding/ServiceAccount once the ClusterExtension is installed).

- .rhdh/scripts/install-rhdh-catalog-source.sh[928-951]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. --olm-version missing arg 🐞 Bug ☼ Reliability
Description
The new --olm-version option dereferences $2 without checking it exists; with set -u, calling
the script as --olm-version (no value) aborts with an unbound variable error instead of emitting a
controlled usage error. This makes the script fail in a brittle, non-user-friendly way for a common
CLI mistake.
Code

.rhdh/scripts/install-rhdh-catalog-source.sh[R747-755]

+    '--olm-version')
+      if [[ "$2" != "v0" && "$2" != "v1" && "$2" != "auto" ]]; then
+        errorf "Unknown OLM version: $2. Must be v0, v1, or auto."
+        usage
+        exit 1
+      fi
+      OLM_VERSION="$2"
+      shift 1
+      ;;
Relevance

⭐⭐⭐ High

Team frequently hardens shell scripts with input validation under set -euo (e.g., MAX_PARALLEL guard
in PR #2870).

PR-#2870

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new --olm-version parsing branch directly evaluates $2 in a conditional and assigns it, with
no check that a second argument is present.

.rhdh/scripts/install-rhdh-catalog-source.sh[719-755]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The `--olm-version` flag handler uses `$2` unconditionally. Under `set -u`, if the user forgets to pass a value, the script terminates with an unbound variable error rather than printing a clear message.

### Issue Context
Argument parsing is done in a `while`/`case` loop and the new `--olm-version` case checks `$2` directly.

### Fix Focus Areas
- Add a guard before reading `$2` (e.g., `if [[ $# -lt 2 ]]; then errorf "--olm-version requires v0|v1|auto"; usage; exit 1; fi`) or use a safe expansion like `${2:-}`.

- .rhdh/scripts/install-rhdh-catalog-source.sh[719-755]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment on lines +937 to +951
# ClusterRoleBinding granting cluster-admin to the installer SA
CRB_NAME="${OPERATOR_NAME_TO_INSTALL}-installer-binding"
echo "apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: ${CRB_NAME}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: ${SA_NAME}
namespace: ${NAMESPACE_SUBSCRIPTION}
" > "$TMPDIR"/ClusterRoleBinding.yml && invoke_cluster_cli apply -f "$TMPDIR"/ClusterRoleBinding.yml

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Cluster-admin installer binding 🐞 Bug ⛨ Security

In the OLM v1 path, the script creates a ClusterRoleBinding that grants the installer ServiceAccount
the built-in cluster-admin ClusterRole, giving it full cluster-wide privileges. This leaves a
highly privileged credential in-cluster and substantially increases impact if the ServiceAccount
token is leaked or the namespace is compromised.
Agent Prompt
### Issue description
The OLM v1 install flow creates a `ClusterRoleBinding` to `cluster-admin` for the `${OPERATOR_NAME_TO_INSTALL}-installer` ServiceAccount. This grants broad, persistent cluster-admin privileges, which is an unnecessary security risk for a script-driven install path.

### Issue Context
The new OLM v1 code path creates a ServiceAccount and binds it to `cluster-admin` before creating the `ClusterExtension`.

### Fix Focus Areas
- Replace the `cluster-admin` ClusterRoleBinding with least-privilege permissions required by the OLM v1 ClusterExtension installer (use an existing purpose-built ClusterRole if available, otherwise define a custom ClusterRole with only needed verbs/resources).
- If elevated privileges are truly required, gate them behind an explicit flag (e.g. `--grant-cluster-admin`) and/or clean them up after installation completes (delete the ClusterRoleBinding/ServiceAccount once the ClusterExtension is installed).

- .rhdh/scripts/install-rhdh-catalog-source.sh[928-951]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@rhdh-qodo-merge rhdh-qodo-merge Bot added documentation Improvements or additions to documentation enhancement New feature or request labels Jun 23, 2026
@Fortune-Ndlovu

Copy link
Copy Markdown
Member Author

/agentic-review

… the operator-controller namespace and granting image-puller access to necessary service accounts. Update ClusterCatalog and ClusterRoleBinding creation to align with OLM v1 specifications.
debugf "Using operator-controller namespace: ${NAMESPACE_OLM_CONTROLLER}"

# Grant image-puller access to OLM v1 controller SAs so they can pull images from the internal registry
oc policy add-role-to-user system:image-puller system:serviceaccount:${NAMESPACE_CATALOGD}:catalogd-controller-manager -n rhdh || true

# Grant image-puller access to OLM v1 controller SAs so they can pull images from the internal registry
oc policy add-role-to-user system:image-puller system:serviceaccount:${NAMESPACE_CATALOGD}:catalogd-controller-manager -n rhdh || true
oc policy add-role-to-user system:image-puller system:serviceaccount:${NAMESPACE_OLM_CONTROLLER}:operator-controller-controller-manager -n rhdh || true
" > "$TMPDIR"/ClusterRoleBinding.yml && invoke_cluster_cli apply -f "$TMPDIR"/ClusterRoleBinding.yml

# Grant installer SA image-puller access so it can pull operator images from the internal registry
oc policy add-role-to-user system:image-puller system:serviceaccount:${NAMESPACE_SUBSCRIPTION}:${SA_NAME} -n rhdh || true
@sonarqubecloud

Copy link
Copy Markdown

@Fortune-Ndlovu

Fortune-Ndlovu commented Jun 23, 2026

Copy link
Copy Markdown
Member Author
image
apiVersion: olm.operatorframework.io/v1
kind: ClusterExtension
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"olm.operatorframework.io/v1","kind":"ClusterExtension","metadata":{"annotations":{},"name":"rhdh"},"spec":{"install":{"preflight":{"crdUpgradeSafety":{"enforcement":"None"}}},"namespace":"rhdh-operator","serviceAccount":{"name":"rhdh-installer"},"source":{"catalog":{"channels":["fast"],"packageName":"rhdh","selector":{"matchLabels":{"olm.operatorframework.io/metadata.name":"rhdh-fast"}}},"sourceType":"Catalog"}}}
  creationTimestamp: '2026-06-23T05:57:28Z'
  finalizers:
    - olm.operatorframework.io/cleanup-unpack-cache
    - olm.operatorframework.io/cleanup-contentmanager-cache
  generation: 1
  managedFields:
    - apiVersion: olm.operatorframework.io/v1
      fieldsType: FieldsV1
      fieldsV1:
        'f:spec':
          .: {}
          'f:install':
            .: {}
            'f:preflight':
              .: {}
              'f:crdUpgradeSafety':
                .: {}
                'f:enforcement': {}
          'f:namespace': {}
          'f:serviceAccount':
            .: {}
            'f:name': {}
          'f:source':
            .: {}
            'f:catalog':
              .: {}
              'f:channels': {}
              'f:packageName': {}
              'f:selector': {}
              'f:upgradeConstraintPolicy': {}
            'f:sourceType': {}
      manager: kubectl-client-side-apply
      operation: Update
      time: '2026-06-23T05:57:28Z'
    - apiVersion: olm.operatorframework.io/v1
      fieldsType: FieldsV1
      fieldsV1:
        'f:metadata':
          'f:finalizers':
            .: {}
            'v:"olm.operatorframework.io/cleanup-contentmanager-cache"': {}
            'v:"olm.operatorframework.io/cleanup-unpack-cache"': {}
      manager: operator-controller
      operation: Update
      time: '2026-06-23T05:57:28Z'
    - apiVersion: olm.operatorframework.io/v1
      fieldsType: FieldsV1
      fieldsV1:
        'f:status':
          .: {}
          'f:conditions':
            .: {}
            'k:{"type":"BundleDeprecated"}':
              .: {}
              'f:lastTransitionTime': {}
              'f:message': {}
              'f:observedGeneration': {}
              'f:reason': {}
              'f:status': {}
              'f:type': {}
            'k:{"type":"ChannelDeprecated"}':
              .: {}
              'f:lastTransitionTime': {}
              'f:message': {}
              'f:observedGeneration': {}
              'f:reason': {}
              'f:status': {}
              'f:type': {}
            'k:{"type":"Deprecated"}':
              .: {}
              'f:lastTransitionTime': {}
              'f:message': {}
              'f:observedGeneration': {}
              'f:reason': {}
              'f:status': {}
              'f:type': {}
            'k:{"type":"Installed"}':
              .: {}
              'f:lastTransitionTime': {}
              'f:message': {}
              'f:observedGeneration': {}
              'f:reason': {}
              'f:status': {}
              'f:type': {}
            'k:{"type":"PackageDeprecated"}':
              .: {}
              'f:lastTransitionTime': {}
              'f:message': {}
              'f:observedGeneration': {}
              'f:reason': {}
              'f:status': {}
              'f:type': {}
            'k:{"type":"Progressing"}':
              .: {}
              'f:lastTransitionTime': {}
              'f:message': {}
              'f:observedGeneration': {}
              'f:reason': {}
              'f:status': {}
              'f:type': {}
          'f:install':
            .: {}
            'f:bundle':
              .: {}
              'f:name': {}
              'f:version': {}
      manager: operator-controller
      operation: Update
      subresource: status
      time: '2026-06-23T05:57:30Z'
  name: rhdh
  resourceVersion: '68186'
  uid: 2d7d5cc0-00f2-4e39-88e2-e2beb6435ef4
spec:
  install:
    preflight:
      crdUpgradeSafety:
        enforcement: None
  namespace: rhdh-operator
  serviceAccount:
    name: rhdh-installer
  source:
    catalog:
      channels:
        - fast
      packageName: rhdh
      selector:
        matchLabels:
          olm.operatorframework.io/metadata.name: rhdh-fast
      upgradeConstraintPolicy: CatalogProvided
    sourceType: Catalog
status:
  conditions:
    - lastTransitionTime: '2026-06-23T05:57:28Z'
      message: ''
      observedGeneration: 1
      reason: Deprecated
      status: 'False'
      type: Deprecated
    - lastTransitionTime: '2026-06-23T05:57:28Z'
      message: ''
      observedGeneration: 1
      reason: Deprecated
      status: 'False'
      type: PackageDeprecated
    - lastTransitionTime: '2026-06-23T05:57:28Z'
      message: ''
      observedGeneration: 1
      reason: Deprecated
      status: 'False'
      type: ChannelDeprecated
    - lastTransitionTime: '2026-06-23T05:57:28Z'
      message: ''
      observedGeneration: 1
      reason: Deprecated
      status: 'False'
      type: BundleDeprecated
    - lastTransitionTime: '2026-06-23T05:57:30Z'
      message: 'Installed bundle image-registry.openshift-image-registry.svc:5000/rhdh/rhdh-operator-bundle:1c0403295d17349851e0ddc12124b93fb18780c1f6869c27eddcf1ced8c1b673 successfully'
      observedGeneration: 1
      reason: Succeeded
      status: 'True'
      type: Installed
    - lastTransitionTime: '2026-06-23T05:57:30Z'
      message: Desired state reached
      observedGeneration: 1
      reason: Succeeded
      status: 'True'
      type: Progressing
  install:
    bundle:
      name: rhdh-operator.v1.10.2
      version: 1.10.2

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

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants