Skip to content

feat: add cluster_overview domain with cluster architecture overview rule - #95

Open
izackv wants to merge 2 commits into
RedHatInsights:mainfrom
izackv:feature/cluster-architecture-overview
Open

feat: add cluster_overview domain with cluster architecture overview rule#95
izackv wants to merge 2 commits into
RedHatInsights:mainfrom
izackv:feature/cluster-architecture-overview

Conversation

@izackv

@izackv izackv commented Jul 6, 2026

Copy link
Copy Markdown

What

Adds a new cluster_overview domain with one informational rule, cluster_architecture_overview — an OrchestratorRule that collects a read-only snapshot of what the cluster is and returns it as structured system_info on an INFO result:

  • Cluster identity: OCP version, channel, cluster ID, upgrade history (last 12), platform, infrastructure name, API URL, base domain
  • Topology: node count, nodes by role, control-plane/infrastructure topology (detects SNO/compact), kubelet versions, OS images
  • Network: CNI type, cluster/service CIDRs, cluster network MTU
  • Storage: storage classes with provisioners and default flag
  • Identity providers: names and types only (no credentials or secret data)
  • Operators: installed subscriptions (name, namespace, channel, installed CSV) — reuses the existing oc_api.get_operator_subscriptions()

The short human-readable summary lands in the result message, e.g.:

OpenShift 4.16.21 on BareMetal | 6 nodes (3x control-plane, 3x master, 3x worker) | CNI: OVNKubernetes | operators: 12

Why

The framework validates health in depth, but the JSON output contains no picture of what the cluster is — useful context when reading results from an unfamiliar cluster, comparing clusters, or attaching results to a support case. This follows the precedent of the hw_fw_details domain (inventory collection rather than pass/fail validation), at the cluster-API level.

This is contributed from my ocp-analyzer project, which generates an equivalent architecture overview offline from collected oc outputs; this port queries the live cluster API via oc_api instead and adds insights available live (e.g. controlPlaneTopology for SNO/compact detection).

Design notes

  • All queries are read-only oc get ... -o json cluster-API calls — no node access, no debug pods, no Secret/ConfigMap data. IdP reporting includes only name and type.
  • ClusterVersion is required: if it cannot be read, UnExpectedSystemOutput propagates and the rule reports SKIP.
  • Every other section degrades gracefully (RBAC-denied or absent resources yield partial data) so a restricted account still gets a useful overview.
  • Runs in --light-run (a handful of API calls), profile general.
  • links currently points to the OpenShift architecture docs; happy to update it to a Confluence page if a maintainer can create one under the In-Cluster Checks Rules space (I don't have access).

Tests

  • tests/rules/cluster_overview/test_architecture_overview.py: full-cluster INFO scenario (message + complete system_info structure assertions), partial/RBAC-restricted scenario, and required-resource-failure scenario via RuleTestBase.
  • tests/domains/test_cluster_overview_domain.py: domain registration.
  • pytest: 849 passed; coverage 89% (above the 80% gate). pre-commit run --all-files: all hooks pass.
  • Verified --list-domains / --list-rules auto-discover the new domain.
  • Not yet run against a live cluster from this environment — I can follow up with --debug-rule cluster_architecture_overview output from a lab cluster if desired.

Possible follow-ups

If this shape works for the project, I'd like to contribute further posture rules from ocp-analyzer (backup/DR detection, upgrade-readiness: zero-disruption PDBs / failurePolicy=Fail webhooks, tenancy governance coverage).

Summary by CodeRabbit

  • New Features

    • Added a new cluster overview view that collects a read-only snapshot of cluster version, topology, networking, storage, identity providers, and installed operators.
    • Added a user-friendly summary line for quick cluster status review.
  • Bug Fixes

    • Improved behavior when some cluster resources are unavailable, allowing partial information to still be shown.
    • Ensured the cluster version check is required so incomplete data is handled consistently.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@izackv, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 31 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 6ef90282-d615-45cc-b0ed-688e5cce6107

📥 Commits

Reviewing files that changed from the base of the PR and between e70ed1c and 638dbf5.

📒 Files selected for processing (6)
  • src/in_cluster_checks/domains/cluster_overview_domain.py
  • src/in_cluster_checks/rules/cluster_overview/__init__.py
  • src/in_cluster_checks/rules/cluster_overview/architecture_overview.py
  • tests/domains/test_cluster_overview_domain.py
  • tests/rules/cluster_overview/__init__.py
  • tests/rules/cluster_overview/test_architecture_overview.py
📝 Walkthrough

Walkthrough

This PR adds a new ClusterOverviewDomain and ClusterArchitectureOverview rule that collects a read-only architecture snapshot (version, topology, network, storage, identity providers, operators) from an OpenShift cluster via oc commands, along with unit tests for both the domain and the rule covering full, partial, and denied-access scenarios.

Changes

Cluster Overview Feature

Layer / File(s) Summary
Domain registration
src/in_cluster_checks/domains/cluster_overview_domain.py, tests/domains/test_cluster_overview_domain.py
ClusterOverviewDomain implements RuleDomain, returning "cluster_overview" as its name and ClusterArchitectureOverview as its rule class; tests verify both.
Rule entry point and orchestration
src/in_cluster_checks/rules/cluster_overview/architecture_overview.py
ClusterArchitectureOverview.run_rule assembles an overview dict via collector helpers and returns an INFO RuleResult; _get_resource wraps oc get calls with required/optional error handling.
Identity, topology, and network collectors
src/in_cluster_checks/rules/cluster_overview/architecture_overview.py
Collects cluster version/platform identity, node topology and roles, kubelet/OS versions, and CNI/network CIDR details.
Storage, identity providers, operators, and summary
src/in_cluster_checks/rules/cluster_overview/architecture_overview.py
Collects storage classes with default flags, OAuth identity providers, sorted operator subscriptions, and builds a one-line human-readable summary.
Rule tests and fixtures
tests/rules/cluster_overview/test_architecture_overview.py
Fixtures and scenarios cover full-access, partial RBAC-denied, and clusterversion-denied cases; tests validate scenario info, skip behavior, and the exact system_info structure.

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

Sequence Diagram(s)

sequenceDiagram
  participant Rule as ClusterArchitectureOverview
  participant OC as oc CLI
  participant API as oc_api
  Rule->>OC: _get_resource(clusterversion, required=True)
  OC-->>Rule: clusterversion JSON
  Rule->>Rule: _collect_cluster_identity(infrastructure)
  Rule->>Rule: _collect_topology(infrastructure)
  Rule->>Rule: _collect_network()
  Rule->>Rule: _collect_storage()
  Rule->>Rule: _collect_identity_providers()
  Rule->>API: _collect_operators()
  API-->>Rule: subscriptions or UnExpectedSystemOutput
  Rule->>Rule: _build_summary(overview)
  Rule-->>Rule: RuleResult.info(summary, overview)
Loading

Related Issues: None specified

Related PRs: None specified

Suggested labels: enhancement, new-rule

Suggested reviewers: None specified

🐰 A rabbit peeked inside the cluster's core,
Counted nodes and versions galore,
Storage classes, CNI, and OAuth too,
One tidy snapshot, read-only view,
Hopping through checks forevermore.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the new cluster_overview domain and its cluster architecture overview rule.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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

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.

@codecov-commenter

codecov-commenter commented Jul 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@c1f220b). Learn more about missing BASE report.

Additional details and impacted files
@@           Coverage Diff           @@
##             main      #95   +/-   ##
=======================================
  Coverage        ?   88.23%           
=======================================
  Files           ?       58           
  Lines           ?     6409           
  Branches        ?        0           
=======================================
  Hits            ?     5655           
  Misses          ?      754           
  Partials        ?        0           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 4

🧹 Nitpick comments (1)
tests/rules/cluster_overview/test_architecture_overview.py (1)

108-178: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding coverage for non-RBAC "missing resource" scenarios.

The scenarios cover a full healthy cluster and RBAC-denied access, but there's no scenario where oc get succeeds with an empty/absent result (e.g. no oauth resource configured, empty storageclass list, or a node lacking any recognized role label). These are distinct from RBAC denial and exercise different code paths in the collector helpers (e.g. empty lists/dicts vs. None from a failed command). As per path instructions, tests should "cover all meaningful scenarios including edge cases, partial state, empty output, and missing resources."

🤖 Prompt for 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.

In `@tests/rules/cluster_overview/test_architecture_overview.py` around lines 108
- 178, The test coverage in TestClusterArchitectureOverview only exercises
healthy and RBAC-denied paths, but it misses missing-resource/empty-result
cases. Add a new RuleScenarioParams in scenario_info (or
scenario_unexpected_system_output if needed) that uses oc_cmd_output_dict
entries for successful but empty responses from helpers like
get_cluster_overview data sources, such as an empty oauth, storageclass, or
nodes payload, and verify the info_msg reflects unknown/zero values rather than
a denial. Reuse the existing FULL_CLUSTER_OC_OUTPUTS,
PARTIAL_CLUSTER_OC_OUTPUTS, and SUBSCRIPTIONS_DENIED patterns to locate the
setup, and ensure the new case distinguishes empty results from RBAC failures in
ClusterArchitectureOverview.

Source: Path instructions

🤖 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 `@src/in_cluster_checks/rules/cluster_overview/architecture_overview.py`:
- Around line 1-218: Black formatting is out of sync in
ClusterArchitectureOverview, so the file needs to be reformatted to match the
repo style. Run Black on the architecture_overview.py module and commit the
resulting formatting changes, keeping the logic in methods like run_rule,
_collect_cluster_identity, and _build_summary unchanged.
- Around line 34-36: The architecture_overview rule’s links list points to
public OpenShift docs instead of the required Confluence page. Update the links
field in the cluster_overview rule definition to use the appropriate Confluence
URL, and keep the documentation reference aligned with the domain/category used
by this rule.
- Around line 31-36: The Cluster architecture overview rule is intended to run
only in the general profile, but the rule definition does not declare that
scope. Update the rule class in architecture_overview.py by adding a
supported_profiles declaration alongside the existing objective_hosts,
unique_name, title, and links fields, using the profile name referenced by the
PR (“general”) so the rule is explicitly profile-specific.
- Around line 181-201: The _collect_operators helper is swallowing
UnExpectedSystemOutput from get_operator_subscriptions, which hides oc and JSON
parsing failures. Remove the broad try/except in _collect_operators and let
UnExpectedSystemOutput propagate so the architecture_overview rule can SKIP with
the original diagnostic context; keep the subscription-to-operator mapping and
sorting logic unchanged.

---

Nitpick comments:
In `@tests/rules/cluster_overview/test_architecture_overview.py`:
- Around line 108-178: The test coverage in TestClusterArchitectureOverview only
exercises healthy and RBAC-denied paths, but it misses
missing-resource/empty-result cases. Add a new RuleScenarioParams in
scenario_info (or scenario_unexpected_system_output if needed) that uses
oc_cmd_output_dict entries for successful but empty responses from helpers like
get_cluster_overview data sources, such as an empty oauth, storageclass, or
nodes payload, and verify the info_msg reflects unknown/zero values rather than
a denial. Reuse the existing FULL_CLUSTER_OC_OUTPUTS,
PARTIAL_CLUSTER_OC_OUTPUTS, and SUBSCRIPTIONS_DENIED patterns to locate the
setup, and ensure the new case distinguishes empty results from RBAC failures in
ClusterArchitectureOverview.
🪄 Autofix (Beta)

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: c763cb67-8d4c-4e0d-a9bb-4785347df6b4

📥 Commits

Reviewing files that changed from the base of the PR and between c1f220b and e70ed1c.

📒 Files selected for processing (6)
  • src/in_cluster_checks/domains/cluster_overview_domain.py
  • src/in_cluster_checks/rules/cluster_overview/__init__.py
  • src/in_cluster_checks/rules/cluster_overview/architecture_overview.py
  • tests/domains/test_cluster_overview_domain.py
  • tests/rules/cluster_overview/__init__.py
  • tests/rules/cluster_overview/test_architecture_overview.py

…rule

Add an informational OrchestratorRule that collects a read-only snapshot
of what the cluster is: identity (version, channel, platform, base
domain, version history), topology (nodes by role, control-plane
topology, kubelet/OS versions), network (CNI type, cluster/service
CIDRs, MTU), storage classes, identity providers, and installed
operator subscriptions. Results are returned as structured system_info
on an INFO status.

The ClusterVersion resource is required (rule is skipped if it cannot
be read); all other sections degrade gracefully on RBAC-restricted or
minimal clusters so a partial overview is still reported.

Contributed from the ocp-analyzer project
(https://github.com/izackv/ocp-analyzer), adapted from its offline
architecture-overview report generator to run live via the cluster API.

Assisted-by: Claude Code (Claude Fable 5) <noreply@anthropic.com>
@izackv
izackv force-pushed the feature/cluster-architecture-overview branch from e70ed1c to e35c623 Compare July 6, 2026 18:08
- Declare supported_profiles = {"general"} explicitly on the rule
- Collect operator subscriptions through the same _get_resource() helper
  as other sections: RBAC denial degrades to an empty list, while JSON
  parse failures still propagate as UnExpectedSystemOutput (SKIP with
  diagnostics) instead of being silently swallowed
- Add test scenario for readable-but-empty resources (no IdPs, no
  storage classes, no subscriptions, node without role labels),
  distinct from RBAC denial

Assisted-by: Claude Code (Claude Fable 5) <noreply@anthropic.com>
sprizend-rh

This comment was marked as duplicate.

sprizend-rh

This comment was marked as duplicate.

@sprizend-rh sprizend-rh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nice contribution — the cluster overview concept and graceful degradation design are solid. Two suggestions to align with project patterns:

  1. Use select_resources() instead of run_oc_command + parse_json() — this is the standard resource-querying API used by all other rules. It's a bigger refactor than just swapping the helper (the _collect_* methods need .model access too), but it drops the parse_json dependency and gives you debug logging for free.

  2. Use self._node_executors for role counting — the executors already have pre-computed node_labels from startup. This removes the duplicated _get_node_roles logic and stays consistent with how NodeResourcesCollector handles roles. get_all_nodes() would still be needed for kubelet/OS version data.

Comment on lines +56 to +68
Args:
resource_args: Arguments for `oc get` (e.g. ["network.config", "cluster"])
required: If True, a failed command raises UnExpectedSystemOutput
(rule becomes SKIP); otherwise None is returned

Returns:
Parsed resource dict, or None if unavailable and not required
"""
args = [*resource_args, "-o", "json"]
return_code, output, _ = self.oc_api.run_oc_command("get", args, timeout=45, raise_on_error=required)
if return_code != 0:
return None
return parse_json(output, f"oc get {' '.join(args)}", self.get_host_ip())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The project convention is to use self.oc_api.select_resources() instead of run_oc_command + parse_json(). It returns parsed Python objects (accessed via .model), handles timeouts, logs commands, and provides debug output — all the things this helper reimplements manually.

This is a larger refactor than just swapping _get_resource, since:

  • select_resources() returns openshift-client objects, so all _collect_* methods would change from .get("spec", {}) to .model.spec
  • For list resources (storageclass, subscriptions), it returns a flat list — not a {"items": [...]} dict — so storage_classes.get("items", []) becomes direct iteration
  • Error handling differs: select_resources() raises OpenShiftPythonException on RBAC denial instead of returning a non-zero return code. The _get_resource wrapper would need a try/except for non-required resources
  • Some resources already have dedicated helpers (e.g. self.oc_api.get_all_nodes())

Example of what _get_resource could look like wrapping select_resources:

def _get_resource(self, resource_type, single=True, all_namespaces=False, required=False):
    try:
        return self.oc_api.select_resources(resource_type, single=single, all_namespaces=all_namespaces, timeout=45)
    except Exception:
        if required:
            raise
        return None

And callers would use .model access:

# e.g. in _collect_network:
network_config = self._get_resource("network.config/cluster")
if not network_config:
    return {}
return {
    "network_type": network_config.model.status.get("networkType"),
    ...
}

It's more work upfront but keeps the rule consistent with the rest of the codebase and drops the parse_json dependency.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rule_file="src/in_cluster_checks/rules/cluster_overview/architecture_overview.py"

ast-grep outline "$rule_file" --items all --view expanded
printf '\n--- select_resources definitions and usages ---\n'
rg -n -C 4 '\bselect_resources\s*\(' src tests
printf '\n--- OpenShiftPythonException references ---\n'
rg -n -C 3 '\bOpenShiftPythonException\b' src tests
printf '\n--- complete rule ---\n'
cat -n "$rule_file"

Length of output: 37212


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- OcApiUtils.select_resources ---'
sed -n '23,145p' src/in_cluster_checks/utils/oc_api_utils.py

printf '%s\n' '--- framework exception handling ---'
rg -n -C 5 'UnExpectedSystemOutput|OpenShiftPythonException' src/in_cluster_checks/core src/in_cluster_checks

printf '%s\n' '--- existing narrow exception conversion patterns ---'
rg -n -C 8 'except oc\.OpenShiftPythonException|except OpenShiftPythonException' src/in_cluster_checks/rules src/in_cluster_checks/utils

printf '%s\n' '--- architecture overview tests ---'
sed -n '1,280p' tests/rules/cluster_overview/test_architecture_overview.py

Length of output: 124403


@sprizend-rh, verified. This refactor is valid.

select_resources() provides the command log, timeout, and debug behavior that this rule currently duplicates. The rule should remove parse_json and use resource objects or .as_dict() consistently.

One exception detail is important. select_resources() propagates OpenShiftPythonException, but the runner converts only UnExpectedSystemOutput to the required skip result. Therefore, _get_resource() must:

  • Catch only OpenShiftPythonException.
  • Return None for an optional resource.
  • Convert the exception to UnExpectedSystemOutput for required clusterversion/version.
  • Not catch broad Exception.

The refactor must also update list handling for nodes, storage classes, and subscriptions. It should use get_all_nodes() for nodes. The tests must replace run_oc_command() output fixtures with select_resources() object/list fixtures and OpenShiftPythonException cases.


✏️ Learnings added
Learnt from: sprizend-rh
Repo: RedHatInsights/incluster-checks PR: 95
File: src/in_cluster_checks/rules/cluster_overview/architecture_overview.py:56-68
Timestamp: 2026-08-10T09:28:39.206Z
Learning: For Python in-cluster rules in `src/in_cluster_checks/rules`, use `self.oc_api.select_resources()` for Kubernetes and OpenShift resource queries instead of `run_oc_command()` followed by JSON parsing. `select_resources()` provides command logging, timeouts, and debug output; it returns resource objects for `single=True` and lists of resource objects otherwise. When an optional resource query must degrade gracefully, catch only `openshift_client.OpenShiftPythonException`. When a required query fails, convert that exception to `UnExpectedSystemOutput` so the framework reports the rule as SKIP.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

Comment on lines +124 to +129
@staticmethod
def _get_node_roles(node: dict) -> list:
"""Extract role names from a node's node-role.kubernetes.io/* labels."""
labels = node.get("metadata", {}).get("labels", {})
roles = [label.split("/", 1)[1] for label in labels if label.startswith("node-role.kubernetes.io/")]
return sorted(roles) if roles else ["unknown"]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This duplicates role-extraction logic from NodeExecutorFactory._get_role_labels_string() (same label prefix parsing and sorting). Since OrchestratorRule has access to self._node_executors, the topology section can read roles from executor.node_labels — the same pattern NodeResourcesCollector._extract_roles() uses in resources_utilization.py.

You'd still need get_all_nodes() for kubeletVersion/osImage (not stored on executors), but the role counting can come from the executors, which removes this static method entirely.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- candidate files ---'
fd -t f -a 'resources_utilization.py|node_executor.*\.py|node_executor_factory.*\.py|architecture_overview.py' . | sort

echo '--- matching definitions and uses ---'
rg -n -C 5 \
  'def _get_role_labels_string\b|def _extract_roles\b|_node_executors\b|node_labels\b|def get_all_nodes\b' \
  src tests || true

echo '--- outlines ---'
for file in \
  src/in_cluster_checks/rules/cluster_overview/architecture_overview.py \
  src/in_cluster_checks/utils/resources_utilization.py
do
  if [ -f "$file" ]; then
    ast-grep outline "$file" --items all
  fi
done

Length of output: 69138


@sprizend-rh, the executor labels use the same sorted role names. However, NodeExecutorFactory creates an executor only when a Node has an internal IP.

This rule obtains node_count from oc get nodes. If it obtains nodes_by_role from self._node_executors, a Node without an internal IP can be included in node_count but omitted from its role count.

I recommend retaining the API Node list as the source for both fields. Extract the common node-role.kubernetes.io/* parsing into a shared utility, then use that utility from NodeExecutorFactory and ClusterArchitectureOverview. This removes duplication without changing the overview snapshot semantics.


✏️ Learnings added
Learnt from: sprizend-rh
Repo: RedHatInsights/incluster-checks PR: 95
File: src/in_cluster_checks/rules/cluster_overview/architecture_overview.py:124-129
Timestamp: 2026-08-10T09:28:46.098Z
Learning: In `src/in_cluster_checks/core/executor_factory.py`, `NodeExecutorFactory` creates a `NodeExecutor` only when the Kubernetes Node has an internal IP. For cluster-wide inventory rules, `self._node_executors` can omit Nodes that `oc get nodes` returns, so the API Node list must remain the source for complete node counts and role aggregation.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

@sprizend-rh sprizend-rh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Two more items:

  1. _collect_operators reimplements get_operator_subscriptions() — there's an existing helper on oc_api that does the same query, already used by two rules in k8s_validations.py.

  2. Grammar nit"1 nodes" in the summary when there's a single node.


def _collect_operators(self) -> list:
"""Collect installed operator subscriptions (name, namespace, channel, CSV)."""
subscriptions = self._get_resource(["subscriptions.operators.coreos.com", "--all-namespaces"])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

self.oc_api.get_operator_subscriptions() already does exactly this — same resource type, same --all-namespaces, same parse_json. It's used by two rules in k8s_validations.py. You could call it directly instead of going through _get_resource:

def _collect_operators(self) -> list:
    subscriptions = self.oc_api.get_operator_subscriptions()
    # ... rest of the mapping logic

Note: get_operator_subscriptions() raises UnExpectedSystemOutput on failure (no graceful degradation), so you'd need a try/except if you want RBAC denial to degrade to an empty list.

return (
f"OpenShift {identity.get('version') or 'unknown'} "
f"on {identity.get('platform') or 'unknown platform'} | "
f"{topology.get('node_count', 0)} nodes ({roles_summary or 'roles unknown'}) | "

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nit: when node_count == 1, this produces "1 nodes". Consider:

node_count = topology.get('node_count', 0)
f"{node_count} {'node' if node_count == 1 else 'nodes'} ({roles_summary or 'roles unknown'})"

@sprizend-rh

sprizend-rh commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Merging is currently blocked because the branch protection rule requires verified (signed) commits. Your commits don't have verified signatures.

To fix this, set up GPG or SSH commit signing:

Option 1 — GPG signing:

# Generate a GPG key (if you don't have one)
gpg --full-generate-key

# Get your key ID
gpg --list-secret-keys --keyid-format=long

# Configure git to use it
git config --global user.signingkey <YOUR_KEY_ID>
git config --global commit.gpgsign true

# Add the public key to GitHub: Settings → SSH and GPG keys → New GPG key
gpg --armor --export <YOUR_KEY_ID>

Option 2 — SSH signing (simpler if you already have an SSH key on GitHub):

git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true

Make sure the same SSH key is added as a signing key in GitHub (Settings → SSH and GPG keys → New SSH key → Key type: Signing Key).

Then re-sign your commits:

# Rebase and re-sign the commits on this branch
git rebase --exec 'git commit --amend --no-edit -S' main

# Force-push the signed commits
git push --force-with-lease

@coderabbitai

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@sprizend-rh The signing procedure is correct.

I cannot configure a signing key, rewrite commits, or force-push this branch. A repository contributor must re-sign the commits and run git push --force-with-lease from an authorized environment.

After GitHub marks the replacement commits as Verified, the branch protection check should allow the merge.

🐇⚠️


✏️ Learnings added
Learnt from: sprizend-rh
URL: https://github.com/RedHatInsights/incluster-checks/pull/95

Timestamp: 2026-08-10T09:32:47.565Z
Learning: Pull request `#95` in the RedHatInsights/incluster-checks repository requires GitHub-verified signed commits before merge. Contributors must sign rewritten commits with a GitHub-registered GPG or SSH signing key and force-push the replacement commit history.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants