Skip to content

feat: add 'make doctor' configuration preflight#93

Open
fonta-rh wants to merge 15 commits into
openshift-eng:mainfrom
fonta-rh:make-doctor
Open

feat: add 'make doctor' configuration preflight#93
fonta-rh wants to merge 15 commits into
openshift-eng:mainfrom
fonta-rh:make-doctor

Conversation

@fonta-rh

@fonta-rh fonta-rh commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds make doctor — a read-only preflight that validates required tools, SSH keys, Ansible collections, and every configuration file, printing a concrete fix command for each problem found
  • Supports targeted mode: make doctor <cluster-type> (e.g. make doctor fencing-kcli) promotes method-specific warnings to failures
  • Adds "Run 'make doctor' to check your setup" hints to deploy-cluster.sh error paths

Details

doctor.sh checks:

  • Required CLI tools (make, aws, jq, rsync, ansible-playbook, go, timeout)
  • SSH public key availability
  • Ansible collection installation
  • inventory.ini presence and validity
  • instance.env sourcing and AWS credential liveness
  • Per-topology dev-scripts configs + CI token validation
  • kcli pull-secret presence and divergence detection

All fix hints point to the central config/ folder introduced by #92.

Dependencies

Depends on #92 (config-folder). Based on that branch; once #92 merges, this PR's diff will reduce to just the doctor changes.

Test plan

  • make doctor with no config files — all sections NOTE/WARN, exit 0
  • make doctor fencing-kcli — kcli pull-secret promoted to FAIL, exit 1
  • make doctor bogus-type — Makefile rejects with clear error
  • make help — doctor appears in help text
  • shellcheck deploy/openshift-clusters/scripts/doctor.sh — clean

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a read-only make doctor preflight check, with optional stricter validation for specific cluster types.
    • Introduced centralized config/ handling with make sync-config to automatically propagate configuration to the locations used by deployments.
    • Added an ansible-lint check to the verification workflow (containerized execution with syntax checks).
  • Bug Fixes

    • Improved validation to fail fast on invalid setup inputs and provide clearer remediation guidance.
  • Documentation

    • Updated setup and role READMEs to reflect the new canonical config/ locations and sync-first workflow, including pre-commit and lint/verify instructions.

fonta-rh and others added 3 commits July 8, 2026 17:30
Add hack/ansible-lint.sh following the containerized pattern of the
existing hack scripts. It runs ansible-lint (configured by the new
.ansible-lint, with pre-existing findings baselined in a skip list) and
ansible-playbook --syntax-check for every playbook, caching the required
collections in ~/.cache/tnt-ansible-collections.

Also validate topology and method in setup.yml pre_tasks regardless of
interactive_mode, so bad -e values fail fast instead of erroring deep
inside a role.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUa6KmTHSMNx9SibfQrLNm
Add a single config/ folder at the repository root where users create
their configuration files. Every make-accessible script syncs config/
files to their canonical locations (newer-wins, never deletes) before
doing anything else; the playbooks and roles keep reading from the
canonical locations unchanged.

- Move all templates and example configs into config/ (single source):
  instance.env.template, config_*_example.sh, kcli.yml.template,
  assisted.yml.template, plus a README map and a .gitignore that
  prevents real config files (including the pull secret) from ever
  being committed
- Add sync_config_files() to aws-hypervisor/scripts/common.sh with a
  manifest of src:dest[:dest2] entries; the pull secret fans out to
  both the dev-scripts and kcli role locations
- Restructure common.sh so the sync runs before instance.env is
  sourced, and replace the raw missing-file source error with an
  actionable hint
- Add 'make sync-config' for users who invoke ansible-playbook
  directly, and register init-host.yml.local in the manifest (it is
  consumed by init-host.yml)
- Source common.sh from get-tnf-logs.sh and helpers/keep-instance.sh
  so every make target inherits the sync
- Gitignore vars/assisted.yml (contains credentials, was not ignored)
- Update all docs and the /setup command to point at config/, keeping
  a note that editing canonical locations directly still works

Backwards compatible: config/ ships with templates only, so the sync
is a no-op until the user explicitly creates a file there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mk1uuxv3YkApKm8w55Kxjd
… guard

Move common.sh from deploy/aws-hypervisor/scripts/ to deploy/ so all
deploy paths (AWS and baremetal) can source it without reaching into
aws-hypervisor internals. Add config_baremetal_fencing.sh to the sync
manifest and have baremetal-adopt generate it into config/. Skip syncing
0-byte source files to prevent accidental credential blanking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openshift-ci openshift-ci Bot requested review from clobrano and jaypoulz July 9, 2026 12:31
@openshift-ci

openshift-ci Bot commented Jul 9, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: fonta-rh

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 Jul 9, 2026
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

This PR centralizes configuration under config/, adds make sync-config and make doctor, introduces containerized ansible-lint validation, updates shared shell loading to deploy/common.sh, and revises deployment and setup documentation to match the new workflow.

Changes

Central config, validation, and tooling

Layer / File(s) Summary
Config layout and docs
config/.gitignore, config/README.md, README.md, CLAUDE.md, deploy/README.md, deploy/aws-hypervisor/README.md, deploy/openshift-clusters/README*.md, deploy/openshift-clusters/roles/*/README.md, .claude/commands/setup*.md, deploy/openshift-clusters/assisted-install.yml
Documentation and ignore rules move canonical inputs to config/, describe sync behavior, and update setup instructions and file references.
Common shell and Makefile wiring
deploy/common.sh, deploy/Makefile, helpers/keep-instance.sh, deploy/aws-hypervisor/scripts/*, deploy/openshift-clusters/scripts/*
Shared shell logic now resolves repository paths and syncs config files, while deployment scripts and make targets switch to the new shared entrypoint and config sync flow.
Doctor preflight checks
deploy/openshift-clusters/scripts/doctor.sh, deploy/openshift-clusters/setup.yml
A new read-only preflight script validates tools, keys, inventory, AWS, dev-scripts, kcli, and assisted installer inputs, and setup adds topology/method assertions.
Ansible lint workflow
.ansible-lint, hack/ansible-lint.sh, Makefile, hack/pre-commit
Ansible linting is configured with offline/mock-role settings, wrapped in a containerized script, wired into make verify, and surfaced in pre-commit guidance.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested labels: ready-for-human-review

Suggested reviewers: jerpeter1


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (3 errors, 2 warnings)

Check name Status Explanation Resolution
No-Weak-Crypto ❌ Error doctor.sh compares pull-secret.json files with cmp -s, a non-constant-time secret comparison. Avoid bytewise comparison of pull-secret contents; validate presence/JSON only, or switch to a constant-time secret compare helper.
No-Sensitive-Data-In-Logs ❌ Error doctor.sh logs raw instance.env source errors and prints values like spoke_cluster_name/AWS_PROFILE, which can expose config contents or internal names. Mask those values and emit only generic failures or file/line references; do not print sourced stderr or config values in logs.
No-Hardcoded-Secrets ❌ Error PR adds hardcoded credential defaults in config templates (bmc_password: admin123, spoke_bmc_password: password). Replace those literals with non-secret placeholders or generated values, and keep only documentation/examples—not committed credentials—in templates.
Docstring Coverage ⚠️ Warning Docstring coverage is 17.65% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Ai-Attribution ⚠️ Warning AI use is explicit in the PR and commits; commit messages use Co-Authored-By for Claude, but no Assisted-by/Generated-by trailers are present. Replace AI-related Co-Authored-By lines with Red Hat-style Assisted-by or Generated-by trailers, and remove AI co-author trailers from the commits.
✅ Passed checks (6 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 main change: adding a new make doctor configuration preflight.
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.
Container-Privileges ✅ Passed Diff only changes a Makefile and a shell script; no container/K8s manifests or privileged settings (privileged/hostPID/allowPrivilegeEscalation/SYS_ADMIN) appear.
No-Injection-Vectors ✅ Passed No new injection-prone patterns found; new shell commands are quoted or whitelist-validated, and the only bash -c uses a constant string.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot added the ready-for-human-review Indicates a PR has been reviewed by automated tools and is ready for human review label Jul 9, 2026

@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: 3

🧹 Nitpick comments (1)
deploy/common.sh (1)

38-62: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

mtime-based sync is fragile; consider content comparison and defensive mkdir -p.

sync_config_files() relies purely on -nt (mtime) to decide whether to overwrite a canonical destination. Git checkouts/clones can produce inconsistent mtimes across files (depending on tooling), so a config/ file could appear "newer" than a destination the user intentionally edited directly, silently overwriting their edits — despite the comment stating "direct edits to the canonical locations are never overwritten."

Additionally, cp "${src}" "${REPO_ROOT}/${dest}" has no mkdir -p "$(dirname ...)" guard; if a destination's parent directory is ever missing, this fails silently (this file is sourced before callers set errexit).

♻️ Suggested hardening
     for dest in "${fields[@]:1}"; do
-      if [[ ! -f "${REPO_ROOT}/${dest}" || "${src}" -nt "${REPO_ROOT}/${dest}" ]]; then
+      if [[ ! -f "${REPO_ROOT}/${dest}" || "${src}" -nt "${REPO_ROOT}/${dest}" ]]; then
+        if [[ -f "${REPO_ROOT}/${dest}" ]] && cmp -s "${src}" "${REPO_ROOT}/${dest}"; then
+          continue  # content identical, skip noisy "updating" message
+        fi
         msg_info "config/${fields[0]} is newer, updating ${dest}"
+        mkdir -p "$(dirname "${REPO_ROOT}/${dest}")"
         cp "${src}" "${REPO_ROOT}/${dest}"
         CONFIG_SYNCED_COUNT=$((CONFIG_SYNCED_COUNT + 1))
       fi
     done
🤖 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 `@deploy/common.sh` around lines 38 - 62, The sync_config_files() logic
currently uses only mtime (-nt) to decide updates, which can overwrite
user-edited canonical files when mtimes are inconsistent. Update the decision in
sync_config_files() to compare file contents or otherwise verify the destination
hasn’t changed before copying from config/. Also add a defensive mkdir -p for
the destination’s parent directory before the cp in the loop so missing
directories don’t cause a failed sync.
🤖 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 @.ansible-lint:
- Around line 16-17: The mocked roles list is incomplete in the ansible lint
config: mock_roles currently only includes git-user, but the playbooks
referenced by deploy/openshift-clusters/assisted-install.yml and
deploy/openshift-clusters/kcli-install.yml also need assisted/acm-install,
assisted/assisted-spoke, and kcli/kcli-redfish added. Update the mock_roles
section in .ansible-lint so these additional roles are included alongside
git-user.

In `@deploy/common.sh`:
- Around line 65-70: Keep the optional source in common.sh, but add an upfront
REGION presence check in the AWS-only entrypoints create.sh, start.sh, stop.sh,
and destroy.sh before any later use of instance.env-derived values. Use those
scripts’ main entry flow to fail fast with a clear message pointing to make
doctor and the instance.env.template setup, so they exit cleanly instead of
reaching an unbound variable error.

In `@deploy/openshift-clusters/scripts/doctor.sh`:
- Around line 243-247: The instance.env sourcing check in doctor.sh is capturing
the error output incorrectly, leaving SOURCE_ERR empty in the failure path.
Update the source command inside the if block so stderr is captured into
SOURCE_ERR before any output is redirected away, and keep the existing
check_pass/check_fail flow intact. Use the surrounding source "${INSTANCE_ENV}"
validation logic to locate the fix and ensure the [FAIL] message includes the
actual parse error text.

---

Nitpick comments:
In `@deploy/common.sh`:
- Around line 38-62: The sync_config_files() logic currently uses only mtime
(-nt) to decide updates, which can overwrite user-edited canonical files when
mtimes are inconsistent. Update the decision in sync_config_files() to compare
file contents or otherwise verify the destination hasn’t changed before copying
from config/. Also add a defensive mkdir -p for the destination’s parent
directory before the cp in the loop so missing directories don’t cause a failed
sync.
🪄 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: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 010ceb8c-038a-43fe-9e85-87606ff36890

📥 Commits

Reviewing files that changed from the base of the PR and between 5ecce91 and ff528c1.

📒 Files selected for processing (51)
  • .ansible-lint
  • .claude/commands/setup.md
  • .claude/commands/setup/README.md
  • CLAUDE.md
  • Makefile
  • README.md
  • config/.gitignore
  • config/README.md
  • config/assisted.yml.template
  • config/config_arbiter_example.sh
  • config/config_fencing_example.sh
  • config/config_sno_example.sh
  • config/instance.env.template
  • config/kcli.yml.template
  • deploy/Makefile
  • deploy/README.md
  • deploy/aws-hypervisor/README.md
  • deploy/aws-hypervisor/scripts/create.sh
  • deploy/aws-hypervisor/scripts/destroy.sh
  • deploy/aws-hypervisor/scripts/force-stop.sh
  • deploy/aws-hypervisor/scripts/init.sh
  • deploy/aws-hypervisor/scripts/inventory.sh
  • deploy/aws-hypervisor/scripts/print_instance_data.sh
  • deploy/aws-hypervisor/scripts/ssh.sh
  • deploy/aws-hypervisor/scripts/start.sh
  • deploy/aws-hypervisor/scripts/stop.sh
  • deploy/common.sh
  • deploy/openshift-clusters/.gitignore
  • deploy/openshift-clusters/README-external-host.md
  • deploy/openshift-clusters/README-kcli.md
  • deploy/openshift-clusters/README.md
  • deploy/openshift-clusters/assisted-install.yml
  • deploy/openshift-clusters/roles/assisted/assisted-spoke/README.md
  • deploy/openshift-clusters/roles/kcli/kcli-install/README.md
  • deploy/openshift-clusters/scripts/baremetal-adopt.sh
  • deploy/openshift-clusters/scripts/clean-spoke.sh
  • deploy/openshift-clusters/scripts/clean.sh
  • deploy/openshift-clusters/scripts/deploy-baremetal.sh
  • deploy/openshift-clusters/scripts/deploy-cluster.sh
  • deploy/openshift-clusters/scripts/deploy-fencing-assisted.sh
  • deploy/openshift-clusters/scripts/doctor.sh
  • deploy/openshift-clusters/scripts/full-clean.sh
  • deploy/openshift-clusters/scripts/get-tnf-logs.sh
  • deploy/openshift-clusters/scripts/patch-nodes.sh
  • deploy/openshift-clusters/scripts/redeploy-cluster.sh
  • deploy/openshift-clusters/scripts/shutdown-cluster.sh
  • deploy/openshift-clusters/scripts/startup-cluster.sh
  • deploy/openshift-clusters/setup.yml
  • hack/ansible-lint.sh
  • hack/pre-commit
  • helpers/keep-instance.sh

Comment thread .ansible-lint
Comment thread deploy/common.sh
Comment on lines +65 to +70
if [[ -f "${COMMON_DIR}/aws-hypervisor/instance.env" ]]; then
# shellcheck source=/dev/null
source "${COMMON_DIR}/aws-hypervisor/instance.env"
else
msg_warning "instance.env not found (only needed for AWS hypervisor targets). To create it: cp config/instance.env.template config/instance.env and edit it."
fi

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.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/bash
set -euo pipefail

echo "== deploy/common.sh (relevant section) =="
nl -ba deploy/common.sh | sed -n '1,140p'

echo
echo "== call sites that source deploy/common.sh =="
rg -n --glob '*.sh' 'source .*deploy/common\.sh|source .*common\.sh' deploy

echo
echo "== references to instance.env / REGION in deploy/ =="
rg -n --glob '*.sh' 'instance\.env|REGION|make doctor|nounset|set -u' deploy

Repository: openshift-eng/two-node-toolbox

Length of output: 250


🏁 Script executed:

#!/usr/bin/bash
set -euo pipefail

echo "== deploy/common.sh (lines 1-140) =="
awk 'NR>=1 && NR<=140 { printf "%5d  %s\n", NR, $0 }' deploy/common.sh

echo
echo "== scripts that source deploy/common.sh =="
rg -n --glob '*.sh' 'source .*common\.sh|source .*deploy/common\.sh' deploy

echo
echo "== instance.env / REGION / doctor references under deploy/ =="
rg -n --glob '*.sh' 'instance\.env|REGION|make doctor|nounset|set -u|set -o nounset' deploy

Repository: openshift-eng/two-node-toolbox

Length of output: 21326


🏁 Script executed:

#!/usr/bin/bash
set -euo pipefail

echo "== deploy/openshift-clusters/scripts/doctor.sh (lines 230-255) =="
awk 'NR>=230 && NR<=255 { printf "%5d  %s\n", NR, $0 }' deploy/openshift-clusters/scripts/doctor.sh

echo
echo "== deploy/aws-hypervisor/scripts/start.sh (lines 1-90) =="
awk 'NR>=1 && NR<=90 { printf "%5d  %s\n", NR, $0 }' deploy/aws-hypervisor/scripts/start.sh

echo
echo "== deploy/aws-hypervisor/scripts/stop.sh (lines 1-90) =="
awk 'NR>=1 && NR<=90 { printf "%5d  %s\n", NR, $0 }' deploy/aws-hypervisor/scripts/stop.sh

Repository: openshift-eng/two-node-toolbox

Length of output: 10522


🏁 Script executed:

#!/usr/bin/bash
set -euo pipefail

echo "== AWS entrypoints around common.sh / nounset =="
for f in deploy/aws-hypervisor/scripts/{create.sh,destroy.sh,force-stop.sh,init.sh,inventory.sh,ssh.sh,start.sh,stop.sh}; do
  echo
  echo "### $f"
  awk 'NR>=1 && NR<=25 { printf "%5d  %s\n", NR, $0 }' "$f"
done

echo
echo "== Existing guards for REGION / instance.env =="
rg -n --glob '*.sh' 'REGION:-|AWS_PROFILE:-|instance\.env.*not found|run '\''make doctor'\''|doctor\.sh|check_fail "REGION|check_note "section skipped: instance.env' deploy

Repository: openshift-eng/two-node-toolbox

Length of output: 8963


Keep instance.env optional here, but fail fast in AWS-only entrypoints
deploy/common.sh:65-70 can stay non-fatal for non-AWS flows, but create.sh, start.sh, stop.sh, and destroy.sh should check REGION up front and exit with a clear make doctor/instance.env.template message instead of hitting an unbound variable later.

🤖 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 `@deploy/common.sh` around lines 65 - 70, Keep the optional source in
common.sh, but add an upfront REGION presence check in the AWS-only entrypoints
create.sh, start.sh, stop.sh, and destroy.sh before any later use of
instance.env-derived values. Use those scripts’ main entry flow to fail fast
with a clear message pointing to make doctor and the instance.env.template
setup, so they exit cleanly instead of reaching an unbound variable error.

Source: Learnings

Comment on lines +243 to +247
if SOURCE_ERR=$( (set -e; set +u; source "${INSTANCE_ENV}") 2>&1 >/dev/null ); then
check_pass "instance.env sources cleanly"
else
check_fail "instance.env does not source cleanly: ${SOURCE_ERR}" \
"fix the shell syntax in ${CONFIG_DIR}/instance.env"

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Capture the source error before reporting the failure.

SOURCE_ERR ends up empty here because the redirections send stderr to the terminal instead of into the variable, so the [FAIL] message loses the actual parse error.

🐛 Proposed fix
-  if SOURCE_ERR=$( (set -e; set +u; source "${INSTANCE_ENV}") 2>&1 >/dev/null ); then
+  if SOURCE_ERR=$(
+    {
+      (set -e; set +u; source "${INSTANCE_ENV}") >/dev/null
+    } 2>&1
+  ); then
📝 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
if SOURCE_ERR=$( (set -e; set +u; source "${INSTANCE_ENV}") 2>&1 >/dev/null ); then
check_pass "instance.env sources cleanly"
else
check_fail "instance.env does not source cleanly: ${SOURCE_ERR}" \
"fix the shell syntax in ${CONFIG_DIR}/instance.env"
if SOURCE_ERR=$(
{
(set -e; set +u; source "${INSTANCE_ENV}") >/dev/null
} 2>&1
); then
check_pass "instance.env sources cleanly"
else
check_fail "instance.env does not source cleanly: ${SOURCE_ERR}" \
"fix the shell syntax in ${CONFIG_DIR}/instance.env"
🤖 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 `@deploy/openshift-clusters/scripts/doctor.sh` around lines 243 - 247, The
instance.env sourcing check in doctor.sh is capturing the error output
incorrectly, leaving SOURCE_ERR empty in the failure path. Update the source
command inside the if block so stderr is captured into SOURCE_ERR before any
output is redirected away, and keep the existing check_pass/check_fail flow
intact. Use the surrounding source "${INSTANCE_ENV}" validation logic to locate
the fix and ensure the [FAIL] message includes the actual parse error text.

@lucaconsalvi lucaconsalvi 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.

Thanks for this one, Pablo! The make doctor idea is excellent — a single preflight that tells you exactly what's wrong and how to fix it is the kind of thing that saves everyone time. The config-folder centralization + sync mechanism is a clean design (the newer-wins timestamp logic, the empty-file guard, the manifest-driven approach). And the ansible-lint containerized runner is a nice addition to make verify.

I found a few issues — one will crash at runtime, a couple could hide errors in the diagnostic tool itself — plus some smaller suggestions. Details are inline below.


Summary: 2 critical, 3 high, 4 medium findings.

# Severity File Issue
1 Critical helpers/keep-instance.sh Sources old path that no longer exists after rename — make keep-instance will crash
2 Critical doctor.sh Claims "read-only" but triggers sync_config_files() via common.sh
3 High doctor.sh 2>/dev/null on source common.sh suppresses all stderr, not just the instance.env warning
4 High deploy/common.sh cp exit code unchecked in sync_config_files() — prints "updating" even if copy fails
5 High doctor.sh ansible-galaxy collection list 2>/dev/null hides diagnostics, causes false "missing" cascade
6 Medium hack/ansible-lint.sh Unquoted $CONTAINER_ENGINE and $CONTAINER_IMAGE
7 Medium doctor.sh AWS check conflates timeout error codes 125-127 with auth failures
8 Medium doctor.sh CI token curl check labels all non-zero exits as "network failure"
9 Medium hack/ansible-lint.sh *.yml glob with no nullglob — spurious failure in empty directory

Comment thread helpers/keep-instance.sh Outdated
Comment thread deploy/openshift-clusters/scripts/doctor.sh
Comment thread deploy/openshift-clusters/scripts/doctor.sh Outdated
Comment thread deploy/common.sh Outdated
Comment thread deploy/openshift-clusters/scripts/doctor.sh Outdated
Comment thread hack/ansible-lint.sh Outdated
Comment thread deploy/openshift-clusters/scripts/doctor.sh
Comment thread deploy/openshift-clusters/scripts/doctor.sh
Comment thread hack/ansible-lint.sh
fonta-rh and others added 5 commits July 9, 2026 17:15
Re-running `make baremetal-adopt` after editing config_baremetal_fencing.sh
(e.g. to set CI_TOKEN) would silently overwrite user edits. Back up the
existing file with a timestamped suffix before regenerating.

Co-Authored-By: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix broken common.sh path in keep-instance.sh after move to deploy/
- Handle cp failures in sync_config_files() so errors are reported
  instead of silently incrementing the sync count
- Add nullglob guard in ansible-lint.sh syntax-check loop

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Makefile sync-config: add set -e so cp failures propagate
- deploy-baremetal.sh: source common.sh before strict flags for
  consistent sync error handling across scripts
- ansible-lint.sh: quote $CONTAINER_ENGINE and $CONTAINER_IMAGE

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Callers like doctor.sh that have their own instance.env validation
can suppress the generic warning without blanket stderr suppression.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Playbooks assisted-install.yml and kcli-install.yml reference roles
via the `role:` syntax that ansible-lint resolves at the repo root.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fonta-rh added a commit to fonta-rh/two-node-toolbox-tnf that referenced this pull request Jul 9, 2026
- Remove blanket 2>/dev/null when sourcing common.sh; use
  SKIP_INSTANCE_ENV_WARNING flag for targeted suppression
- ansible-galaxy: capture stderr so config errors surface instead of
  cascading false "missing collection" failures
- timeout exit codes ≥125: report as "could not run" not "credentials
  not working"
- curl: include exit code in CI token check warning

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fonta-rh added a commit to fonta-rh/two-node-toolbox-tnf that referenced this pull request Jul 9, 2026
- Remove blanket 2>/dev/null when sourcing common.sh; use
  SKIP_INSTANCE_ENV_WARNING flag for targeted suppression
- ansible-galaxy: capture stderr so config errors surface instead of
  cascading false "missing collection" failures
- timeout exit codes ≥125: report as "could not run" not "credentials
  not working"
- curl: include exit code in CI token check warning

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fonta-rh and others added 6 commits July 9, 2026 18:11
…quisite

sync_config_files() was called at module scope whenever any script
sourced common.sh, making every import a side-effecting operation.
This forced callers like doctor.sh to opt out via SKIP_CONFIG_SYNC=1.

Move the sync to a Makefile prerequisite on deploy and cluster targets
only, keeping common.sh as a pure library. The sync-config target now
explicitly calls sync_config_files, and only targets that actually
consume synced config files depend on it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Read-only preflight that validates the full deployment configuration
and prints a concrete fix command for every failure: required tools,
SSH key, Ansible collections, inventory.ini, instance.env plus live AWS
credentials, dev-scripts topology configs and pull secret, CI registry
auth cross-check with live CI token check, and the kcli pull secret.
Exits non-zero if and only if a check failed.

- 'make doctor' runs common checks plus every auto-detected section
- 'make doctor <cluster-type>' validates strictly for that deployment:
  the method's section becomes mandatory and its method-specific
  warnings are promoted to failures; cluster-type goals following
  'doctor' are neutralized so they never trigger a deployment
- inventory.ini stays informational when absent (the AWS flow generates
  it via 'make inventory'), but a present-but-unedited copy of the
  sample fails, since that breaks deployment in confusing ways
- deploy-cluster.sh preflight error paths now point at 'make doctor'

Checks are ported from the validation steps in
.claude/commands/setup.md and the in-playbook preflight in
roles/dev-scripts/install-dev/tasks/config.yml.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LSBffYjh3rtLToAMR1pjQT
After the config-folder PR moved common.sh from
deploy/aws-hypervisor/scripts/ to deploy/, doctor.sh referenced the
old path and would fail on startup. Fix the source path and replace
msg_err with an inline printf so the summary works even when
common.sh fails to load.

All fix hints now point to the central config/ folder instead of the
deep canonical paths, consistent with the config-folder workflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Doctor sources common.sh which unconditionally runs sync_config_files,
writing to the working tree. Add SKIP_CONFIG_SYNC guard so doctor
honours its read-only contract. Also add a missing assisted installer
config section so `make doctor fencing-assisted` validates vars/assisted.yml.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove blanket 2>/dev/null when sourcing common.sh; use
  SKIP_INSTANCE_ENV_WARNING flag for targeted suppression
- ansible-galaxy: capture stderr so config errors surface instead of
  cascading false "missing collection" failures
- timeout exit codes ≥125: report as "could not run" not "credentials
  not working"
- curl: include exit code in CI token check warning

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
config-folder's sync refactor moved the sync call from common.sh
module scope to a Makefile prerequisite. SKIP_CONFIG_SYNC is now a
no-op — remove it. Cluster-type sync prerequisites are scoped to the
else branch so 'make doctor <type>' doesn't trigger them.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@fonta-rh

fonta-rh commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review — the findings were spot-on. Here's the status:

Already addressed (in earlier commits, before your review):

  • keep-instance.sh path → was already correct (deploy/common.sh), your suggestion matched the existing code
  • doctor.sh read-only claim → fixed in b5d2a3f via SKIP_CONFIG_SYNC=1

Addressed in 5fefaac (pushed same day as your review):

  • Removed blanket 2>/dev/null on source common.sh → targeted SKIP_INSTANCE_ENV_WARNING flag instead
  • ansible-galaxy: 2>/dev/null2>&1 with explicit failure handling
  • timeout exit codes ≥125: now reported as "could not run" instead of "credentials not working"
  • curl: exit code included in CI token check warning
  • hack/ansible-lint.sh: shopt -s nullglob already in place; variables already quoted at usage sites

New in this push — we went further and addressed the root cause of the read-only concern:

  • Moved sync_config_files() from a common.sh auto-call to a Makefile prerequisite (6b0b331 on config-folder, ceacc18 on make-doctor). common.sh is now a side-effect-free library import. SKIP_CONFIG_SYNC is gone entirely.
  • Also fixed the unchecked cp exit code you flagged — it now uses if ! cp with error logging.

The make-doctor branch has been rebased onto the updated config-folder.

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

🧹 Nitpick comments (1)
deploy/Makefile (1)

78-93: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Verify .PHONY coverage for new targets.

Static analysis flags $(VALID_CLUSTER_TYPES) and baremetal-adopt baremetal-fencing-agent as not declared PHONY. Since sync-config and doctor are also new non-file targets, confirm they're added to the existing .PHONY list (not shown in this diff) to avoid conflicts with any same-named files/directories.

#!/bin/bash
rg -n '^\.PHONY' -A2 deploy/Makefile
🤖 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 `@deploy/Makefile` around lines 78 - 93, The new make targets in the deploy
Makefile are missing or may be missing .PHONY declarations, which can cause
file-name collisions and incorrect no-op behavior. Update the existing .PHONY
list to include the new non-file targets referenced by the rules here,
specifically sync-config, doctor, baremetal-adopt, baremetal-fencing-agent, and
the VALID_CLUSTER_TYPES entries if they are intended as command targets. Verify
the declarations alongside the target definitions so the make behavior stays
consistent even if matching files or directories exist.

Source: Linters/SAST tools

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

Nitpick comments:
In `@deploy/Makefile`:
- Around line 78-93: The new make targets in the deploy Makefile are missing or
may be missing .PHONY declarations, which can cause file-name collisions and
incorrect no-op behavior. Update the existing .PHONY list to include the new
non-file targets referenced by the rules here, specifically sync-config, doctor,
baremetal-adopt, baremetal-fencing-agent, and the VALID_CLUSTER_TYPES entries if
they are intended as command targets. Verify the declarations alongside the
target definitions so the make behavior stays consistent even if matching files
or directories exist.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: ea099182-e229-4fce-ab18-21f1181be307

📥 Commits

Reviewing files that changed from the base of the PR and between ff528c1 and ceacc18.

📒 Files selected for processing (15)
  • .ansible-lint
  • .claude/commands/setup.md
  • CLAUDE.md
  • README.md
  • config/README.md
  • deploy/Makefile
  • deploy/README.md
  • deploy/aws-hypervisor/README.md
  • deploy/common.sh
  • deploy/openshift-clusters/scripts/baremetal-adopt.sh
  • deploy/openshift-clusters/scripts/deploy-baremetal.sh
  • deploy/openshift-clusters/scripts/deploy-cluster.sh
  • deploy/openshift-clusters/scripts/doctor.sh
  • hack/ansible-lint.sh
  • helpers/keep-instance.sh
✅ Files skipped from review due to trivial changes (4)
  • deploy/aws-hypervisor/README.md
  • CLAUDE.md
  • deploy/README.md
  • README.md
🚧 Files skipped from review as they are similar to previous changes (9)
  • helpers/keep-instance.sh
  • .ansible-lint
  • deploy/openshift-clusters/scripts/deploy-baremetal.sh
  • deploy/openshift-clusters/scripts/baremetal-adopt.sh
  • config/README.md
  • hack/ansible-lint.sh
  • deploy/openshift-clusters/scripts/deploy-cluster.sh
  • .claude/commands/setup.md
  • deploy/common.sh

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. ready-for-human-review Indicates a PR has been reviewed by automated tools and is ready for human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants