Skip to content

OSAC-1675: Add runner user setup and fix machine-init for github-runner - #98

Merged
openshift-merge-bot[bot] merged 6 commits into
osac-project:mainfrom
eliorerz:OSAC-1675-machine-init-fixes
Jul 4, 2026
Merged

openshift-merge-bot[bot] merged 6 commits into
osac-project:mainfrom
eliorerz:OSAC-1675-machine-init-fixes

Conversation

@eliorerz

@eliorerz eliorerz commented Jun 24, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Add runner-user step to machine-init.sh: creates github-runner user with libvirt group, passwordless sudo, and SSH authorized_keys from root
  • Fix cluster-tool config path to target ~github-runner instead of ~root, resolving "No server specified" error
  • Fix vault-setup.sh rootless Podman chown using podman unshare
  • Add vault-migrate-secrets.sh for copying KV v2 secrets between Vault instances over SSH

Test plan

  • Run sudo ./scripts/machine-init.sh runner-user on a new CI server and verify user creation
  • Run sudo ./scripts/machine-init.sh cluster-tool and verify config lands under /home/github-runner/.config/cluster-tool/
  • As github-runner, run cluster-tool pull and confirm no "No server specified" error
  • Run vault-setup.sh as non-root and verify podman unshare chown succeeds
  • Run vault-migrate-secrets.sh between two Vault instances

Summary by CodeRabbit

  • New Features

    • Added support for provisioning a dedicated runner user during machine setup.
    • Added a Vault secret migration utility for moving KV secrets between Vault instances.
    • Added a step-by-step guide for setting up a new CI machine and runner environment.
  • Bug Fixes

    • Improved setup reliability for Vault ownership, login persistence, and runner access.
    • Expanded environment cleanup to remove stale network/proxy entries during test setup.

@openshift-ci-robot

openshift-ci-robot commented Jun 24, 2026 •

Copy link
Copy Markdown

@eliorerz: This pull request references OSAC-1675 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "5.0.0" version, but no target version was set.

Details

In response to this:

Summary

  • Add runner-user step to machine-init.sh: creates github-runner user with libvirt group, passwordless sudo, and SSH authorized_keys from root
  • Fix cluster-tool config path to target ~github-runner instead of ~root, resolving "No server specified" error
  • Fix vault-setup.sh rootless Podman chown using podman unshare
  • Add vault-migrate-secrets.sh for copying KV v2 secrets between Vault instances over SSH

Test plan

  • Run sudo ./scripts/machine-init.sh runner-user on a new CI server and verify user creation
  • Run sudo ./scripts/machine-init.sh cluster-tool and verify config lands under /home/github-runner/.config/cluster-tool/
  • As github-runner, run cluster-tool pull and confirm no "No server specified" error
  • Run vault-setup.sh as non-root and verify podman unshare chown succeeds
  • Run vault-migrate-secrets.sh between two Vault instances

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 openshift-eng/jira-lifecycle-plugin repository.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 51 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: Repository: osac-project/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 2cd27368-b54a-4b6e-8e17-cd198f3d4199

📥 Commits

Reviewing files that changed from the base of the PR and between f2a3698 and aedf907.

📒 Files selected for processing (5)
  • .github/workflows/e2e-vmaas.yml
  • docs/new-ci-machine-setup.md
  • scripts/machine-init.sh
  • vault/scripts/vault-migrate-secrets.sh
  • vault/scripts/vault-setup.sh

Walkthrough

Adds a runner-user provisioning step to machine-init.sh (user/group creation, sudoers, SSH keys, sshd/journald hardening, re-homed cluster-tool/Podman config, extended verification), a new Vault secret migration script, additional Vault setup phases for AppRole and linger/backup timer, a haproxy cleanup step in the e2e-vmaas workflow, and a new CI machine setup runbook document.

Changes

Runner Bootstrap and Vault Automation

Layer / File(s) Summary
Runner user and host preparation
scripts/machine-init.sh
Adds RUNNER_USER constant, create_runner_user() (user/group creation, sudoers, deduplicated SSH key provisioning, ownership/permissions), sshd OOM hardening and persistent journaling in services, extended run_verify checks, and step wiring/help text/default step list updates.
cluster-tool and Podman re-homing
scripts/machine-init.sh
Redirects CT_CONFIG_DIR and CONTAINERS_CONF to the runner user's home, hard-fails if the runner user is missing, creates/owns data subdirectories, and repairs a /root/.config/cluster-tool symlink to the runner-owned config.
Vault setup phases and AppRole
vault/scripts/vault-setup.sh
Updates phase documentation, applies data/log ownership in both root and rootless (podman unshare) modes, and adds Phase 10 (AppRole role/credential setup) and Phase 11 (linger + backup timer).
Vault secret migration flow
vault/scripts/vault-migrate-secrets.sh
New script migrating KV v2 secrets between Vault instances over SSH: argument parsing, remote Vault helpers, recursive key listing, preflight checks, per-key fetch/write, and summary/exit status.
Workflow cleanup
.github/workflows/e2e-vmaas.yml
Adds a best-effort cluster-tool cleanup-haproxy step during cluster environment preparation.

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

New CI Machine Setup Runbook

Layer / File(s) Summary
CI machine setup runbook
docs/new-ci-machine-setup.md
Adds a complete runbook covering machine initialization, Vault setup/migration, Podman/runner installation, flavor pulling, verification, cleanup, and troubleshooting.

Estimated code review effort: 1 (Trivial) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Operator
  participant vault-migrate-secrets.sh
  participant SourceVault
  participant DestinationVault

  Operator->>vault-migrate-secrets.sh: run with src-ssh, dst-ssh, init jsons
  vault-migrate-secrets.sh->>SourceVault: vault status -format=json
  vault-migrate-secrets.sh->>DestinationVault: vault status -format=json
  vault-migrate-secrets.sh->>SourceVault: vault kv list -format=json (recursive)
  SourceVault-->>vault-migrate-secrets.sh: leaf key paths

  loop each secret key
    vault-migrate-secrets.sh->>SourceVault: vault kv get -format=json
    SourceVault-->>vault-migrate-secrets.sh: .data.data payload
    vault-migrate-secrets.sh->>DestinationVault: vault kv put key -
    DestinationVault-->>vault-migrate-secrets.sh: write result
  end
  vault-migrate-secrets.sh-->>Operator: migrated/failed summary
Loading

Possibly related PRs

  • osac-project/osac-test-infra#74: Both PRs change vault/scripts/vault-setup.sh to switch Vault auth/role setup to AppRole by enabling approle/ and provisioning the osac-e2e role with role_id/secret_id.
  • osac-project/osac-test-infra#75: Both PRs directly modify scripts/machine-init.sh, with this PR extending the previously added steps (runner-user, cluster-tool path handling, services/verify checks).

Suggested reviewers: eranco74, tzumainn


Caution

Pre-merge checks failed

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

  • Ignore

❌ Failed checks (2 errors, 1 warning)

Check name Status Explanation Resolution
No-Injection-Vectors ❌ Error vault-migrate-secrets.sh builds remote ssh shell strings from KV mount/path/key data, so a crafted secret name can break quoting and run commands. Escape remote args instead of interpolating into a shell string; pass argv safely or use printf %q/ssh -- to avoid shell injection.
No-Sensitive-Data-In-Logs ❌ Error vault-migrate-secrets.sh prints SRC_SSH/DST_SSH and errors with those hostnames, which can leak internal hostnames into logs. Stop logging raw SSH targets; use generic status text or redact hostnames in stdout/stderr.
Ai-Attribution ⚠️ Warning PR range includes AI-attributed commits using Co-Authored-By (e.g. 6339f67, 3d8cba, 313bf2) instead of Red Hat trailers. Replace AI-related Co-Authored-By trailers with Assisted-by or Generated-by in the affected commits, and keep AI-assist notices consistent.
✅ Passed checks (8 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 runner user setup and updating machine-init for github-runner.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
No-Hardcoded-Secrets ✅ Passed No hardcoded secrets found: added tokens/IDs are read from Vault/GitHub outputs, and searches found no literal secret/password assignments, embedded creds, or private keys.
No-Weak-Crypto ✅ Passed Checked the changed files and diff; found no MD5/SHA1/DES/RC4/3DES/Blowfish/ECB, custom crypto, or secret/token comparisons.
Container-Privileges ✅ Passed Only scripts/machine-init.sh changed, and the diff contains no privileged/hostPID/hostNetwork/hostIPC/SYS_ADMIN/allowPrivilegeEscalation settings.
✨ 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.

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

🤖 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 `@scripts/machine-init.sh`:
- Around line 480-485: The runner-user verification in the `id "${RUNNER_USER}"`
block only checks that the user exists, so it misses required setup contract
failures. Update the runner-user validation in `machine-init.sh` to also verify
`libvirt` group membership and passwordless sudo for `${RUNNER_USER}`, and
ensure each missing requirement increments `failures` just like the existing
“user not found” path. Keep the checks and failure handling together so the
summary reflects any contract violation.
- Around line 142-144: The libvirt group membership step can fail because
`usermod -aG libvirt` assumes the group already exists; update the
`machine-init.sh` runner setup to ensure `libvirt` is created before calling
`usermod`, or make the add-to-group logic explicitly depend on the
package/service step that creates it. Use the `RUNNER_USER` setup block with the
`id -nG ... | grep -qw libvirt` check to locate this change and keep the
existing echo flow intact.
- Around line 328-330: The cluster-tool setup in the runner-home configuration
block can expand RUNNER_USER before that user is guaranteed to exist, which may
create invalid paths and later fail during ownership changes. Update the script
around the RUNNER_HOME and CT_CONFIG_DIR setup to either call create_runner_user
first or explicitly fail early with a clear message if the runner user is
missing, so the cluster-tool step never proceeds against a non-existent home
directory.
- Around line 354-356: The ownership step in machine-init.sh is too broad
because the chown in the data directory setup can recursively touch an arbitrary
DATA_PATH from config. Update the directory creation block around mkdir and
chown so only the directories this script creates are chowned, and add
validation in the same flow to reject unsafe DATA_PATH roots before ownership
changes. Use the existing DATA_PATH and RUNNER_USER handling in the machine-init
script to keep the fix localized and avoid recursive ownership of the full
configured path.

In `@vault/scripts/vault-migrate-secrets.sh`:
- Line 161: The vault-migrate-secrets.sh file was auto-modified by
end-of-file-fixer because it is missing the required trailing newline. Update
the script so the file ends with a single EOF newline, then re-run pre-commit
and commit that newline-only change; use the vault-migrate-secrets.sh script as
the target for the fix.
- Around line 157-161: The vault migration script currently prints the failure
count in its completion summary but still exits successfully even when some
secrets fail to migrate. Update the end of vault-migrate-secrets.sh so the main
migration flow returns a non-zero status whenever failed is greater than zero,
keeping the existing summary output in place. Use the script’s final
completion/exit logic near the Migration complete message and the
migrated/failed counters to ensure automation can detect partial migration
failures.
- Around line 43-50: The SSH helpers src_vault and dst_vault are interpolating
sensitive values and user-controlled paths directly into remote shell strings,
which exposes tokens and makes the commands injectable. Update src_vault,
dst_vault, and the call sites that use KV_MOUNT and key so they pass arguments
safely as quoted positional parameters rather than string-expanded fragments,
keeping SRC_TOKEN and DST_TOKEN out of the command text and preventing crafted
mount/key values from altering the remote command.
- Line 59: The secret listing logic in src_vault within vault-migrate-secrets.sh
is swallowing all failures by using a blanket return 0 after kv list, which can
hide real Vault or transport errors. Update the list_output handling so only the
explicit empty/not-found case is treated as “no secrets,” and let other non-zero
exits propagate so the caller can fail appropriately; keep the fix localized
around the kv list invocation and the surrounding migration flow that consumes
list_output.
🪄 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: osac-project/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 7055f95d-b710-49b4-9d7f-16a0dada48dc

📥 Commits

Reviewing files that changed from the base of the PR and between b3c2097 and e4204af.

📒 Files selected for processing (3)
  • scripts/machine-init.sh
  • vault/scripts/vault-migrate-secrets.sh
  • vault/scripts/vault-setup.sh

Comment thread scripts/machine-init.sh
Comment thread scripts/machine-init.sh
Comment thread scripts/machine-init.sh Outdated
Comment thread scripts/machine-init.sh
Comment on lines +43 to +50
# Run a vault command on the source host.
src_vault() {
ssh "$SRC_SSH" "VAULT_ADDR=http://127.0.0.1:8200 VAULT_TOKEN='${SRC_TOKEN}' vault $*"
}

# Run a vault command on the destination host.
dst_vault() {
ssh "$DST_SSH" "VAULT_ADDR=http://127.0.0.1:8200 VAULT_TOKEN='${DST_TOKEN}' vault $*"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the target file and inspect the relevant sections with line numbers.
git ls-files 'vault/scripts/vault-migrate-secrets.sh'
wc -l vault/scripts/vault-migrate-secrets.sh
cat -n vault/scripts/vault-migrate-secrets.sh | sed -n '1,220p'

Repository: osac-project/osac-test-infra

Length of output: 6397


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect shell usage patterns and the referenced line ranges in the script.
rg -n "src_vault|dst_vault|KV_MOUNT|SRC_TOKEN|DST_TOKEN|ssh |vault " vault/scripts/vault-migrate-secrets.sh

Repository: osac-project/osac-test-infra

Length of output: 1661


Avoid interpolating tokens and secret paths into SSH command strings. SRC_TOKEN, DST_TOKEN, KV_MOUNT, and key are expanded into the remote shell at lines 45, 50, 59, and 136-147, which exposes root tokens in process args and lets crafted mount/key values break or inject the remote command. Pass fixed commands with quoted positional arguments instead.

🧰 Tools
🪛 Shellcheck (0.11.0)

[info] 45-45: Note that, unescaped, this expands on the client side.

(SC2029)


[info] 50-50: Note that, unescaped, this expands on the client side.

(SC2029)

🤖 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 `@vault/scripts/vault-migrate-secrets.sh` around lines 43 - 50, The SSH helpers
src_vault and dst_vault are interpolating sensitive values and user-controlled
paths directly into remote shell strings, which exposes tokens and makes the
commands injectable. Update src_vault, dst_vault, and the call sites that use
KV_MOUNT and key so they pass arguments safely as quoted positional parameters
rather than string-expanded fragments, keeping SRC_TOKEN and DST_TOKEN out of
the command text and preventing crafted mount/key values from altering the
remote command.

Sources: Coding guidelines, Linters/SAST tools

local path="$1"
local list_output key

list_output=$(src_vault "kv list -format=json '${KV_MOUNT}/${path}'" 2>/dev/null) || return 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the target file and inspect the relevant section with line numbers.
git ls-files vault/scripts/vault-migrate-secrets.sh
wc -l vault/scripts/vault-migrate-secrets.sh
sed -n '1,140p' vault/scripts/vault-migrate-secrets.sh | cat -n

Repository: osac-project/osac-test-infra

Length of output: 5641


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the remainder of the script around key enumeration and error handling.
sed -n '116,160p' vault/scripts/vault-migrate-secrets.sh | cat -n

# Search for any explicit handling of list failures or "empty/not-found" cases.
rg -n "list_recursive|kv list|not found|empty|return 0|return 1" vault/scripts/vault-migrate-secrets.sh

Repository: osac-project/osac-test-infra

Length of output: 1995


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '116,160p' vault/scripts/vault-migrate-secrets.sh | cat -n
echo '---'
rg -n "list_recursive|kv list|not found|empty|return 0|return 1" vault/scripts/vault-migrate-secrets.sh

Repository: osac-project/osac-test-infra

Length of output: 1999


Handle Vault list failures separately vault/scripts/vault-migrate-secrets.sh:59
|| return 0 turns permission errors, missing mounts, SSH failures, and Vault CLI errors into “no secrets”, so the migration can silently skip data and still report success. Only suppress the explicit empty/not-found case; otherwise propagate a non-zero exit.

🤖 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 `@vault/scripts/vault-migrate-secrets.sh` at line 59, The secret listing logic
in src_vault within vault-migrate-secrets.sh is swallowing all failures by using
a blanket return 0 after kv list, which can hide real Vault or transport errors.
Update the list_output handling so only the explicit empty/not-found case is
treated as “no secrets,” and let other non-zero exits propagate so the caller
can fail appropriately; keep the fix localized around the kv list invocation and
the surrounding migration flow that consumes list_output.

Comment thread vault/scripts/vault-migrate-secrets.sh Outdated
Comment thread vault/scripts/vault-migrate-secrets.sh Outdated
@omer-vishlitzky

Copy link
Copy Markdown
Contributor

💀 CI Triage: broken_main | Category: BOOT

Root cause: Helm upgrade fails because the new osac-operator chart uses valueFrom for OSAC_AAP_TOKEN, which conflicts with the literal value in the snapshot's deployment.

Explanation: PR #328 in osac-installer updated the CI Helm values to use operator.aap.tokenSecret for the AAP token, leveraging a new feature added in osac-operator PR #316. This causes the Helm chart to render valueFrom: secretKeyRef for the OSAC_AAP_TOKEN environment variable. However, the vmaas-helm snapshot flavor contains an older version of the osac-operator deployment where OSAC_AAP_TOKEN was set with a literal value. During the refresh-after-snapshot.py step, helm upgrade attempts to patch the deployment. Because Kubernetes uses a strategic merge patch for env arrays (keyed by name), the new valueFrom field is merged with the existing value field. Kubernetes rejects this with may not be specified when value is not empty, breaking the boot step for all PRs.

Evidence:

build-log.txt:

Error: UPGRADE FAILED: cannot patch "osac-operator" with kind Deployment: Deployment.apps "osac-operator" is invalid: spec.template.spec.containers[0].env[1].valueFrom: Invalid value: "": may not be specified when `value` is not empty

build-log.txt:

subprocess.CalledProcessError: Command '['helm', 'upgrade', 'osac', 'charts/osac/', '--namespace', 'osac-e2e-ci', '--values', 'values/vmaas-ci/values.yaml', '--set', 'service.externalHostname=fulfillment-api-osac-e2e-ci.apps.test-infra-cluster-vmaas-helm.redhat.com', '--set', 'service.internalHostname=fulfillment-internal-api-osac-e2e-ci.apps.test-infra-cluster-vmaas-helm.redhat.com', '--set', 'aap.bootstrap.enabled=false', '--set', 'clusterFulfillment.config.HOSTED_CLUSTER_BASE_DOMAIN=hosted.test-infra-cluster-vmaas-helm.redhat.com', '--timeout', '15m']' returned non-zero exit status 1.

Suggestion: Fix the upgrade path in the osac-operator Helm chart by explicitly setting value: "" when valueFrom is used (this clears the old value during the strategic merge patch). Alternatively, delete the osac-operator deployment in refresh-after-snapshot.py before running helm upgrade, or capture a new snapshot flavor.


Prow job | Build 2069838228420038656 | 🤖 triagent

For deeper investigation, use the /osac-debug-e2e skill with this build ID.

@omer-vishlitzky

Copy link
Copy Markdown
Contributor

💀 CI Triage: broken_main | Category: BOOT

Root cause: Helm upgrade fails during cluster boot because strategic merge patch merges the new 'valueFrom' field into the existing 'osac-operator' deployment's 'OSAC_AAP_TOKEN' env var without clearing the old 'value' field, resulting in an invalid Kubernetes deployment spec.

Explanation: Following the merge of osac-installer PR #328 and PR #329, the CI Helm values (values/vmaas-ci/values.yaml) were updated to configure 'operator.aap.tokenSecret' to read the AAP token from a secret. This causes the new Helm chart to render the 'OSAC_AAP_TOKEN' env var using 'valueFrom.secretKeyRef'. However, the pre-built cluster snapshot (vmaas-helm) contains an older deployment of 'osac-operator' where 'OSAC_AAP_TOKEN' is defined as a literal 'value: ""'. During the boot step's refresh phase, 'refresh-after-snapshot.py' runs 'helm upgrade', which attempts to apply a strategic merge patch to the existing deployment. Because strategic merge patch does not automatically unset the existing 'value' field, the merged env var ends up with both 'value' and 'valueFrom' set, which is rejected by the Kubernetes API server.

Evidence:

build-log.txt:

Error: UPGRADE FAILED: cannot patch "osac-operator" with kind Deployment: Deployment.apps "osac-operator" is invalid: spec.template.spec.containers[0].env[1].valueFrom: Invalid value: "": may not be specified when `value` is not empty

Suggestion: To fix this, either: (1) Update the osac-operator Helm chart deployment template (charts/operator/templates/deployment.yaml) to explicitly set 'value: null' when 'tokenSecret' is used so that strategic merge patch clears the existing 'value' field; (2) Update 'refresh-after-snapshot.py' in osac-installer to delete the existing 'osac-operator' deployment before running 'helm upgrade' to force a clean recreation; or (3) Rebuild and publish new cluster snapshot flavors with the updated Helm chart.


Prow job | Build 2069885393515319296 | 🤖 triagent

For deeper investigation, use the /osac-debug-e2e skill with this build ID.

@eliorerz
eliorerz force-pushed the OSAC-1675-machine-init-fixes branch from 5ea5c38 to fa4c44d Compare June 24, 2026 23:16
@eliorerz
eliorerz force-pushed the OSAC-1675-machine-init-fixes branch from fa4c44d to 05c4b84 Compare June 24, 2026 23:20

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

🤖 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 `@scripts/machine-init.sh`:
- Around line 410-413: The symlink setup in machine-init.sh is deleting the
existing root-owned cluster-tool config before migrating its contents, which can
lose state on reruns. Update the initialization flow around the ROOT_CT_DIR
replacement to first detect and copy any existing config into the runner-owned
CT_CONFIG_DIR, preserving state.json, keys, and server data, and only then
remove the old directory and create the symlink. Keep the change localized to
the config migration block that currently runs rm -rf, mkdir -p /root/.config,
and ln -sfn.

In `@vault/scripts/vault-setup.sh`:
- Around line 232-237: The AppRole setup for osac-e2e leaves the SecretID
effectively unlimited because secret_id_num_uses and secret_id_ttl are set to
zero. Update the vault write auth/approle/role/osac-e2e configuration to bound
SecretID reuse and lifetime, using the existing vault-setup.sh role definition
so the SecretID can’t be reused indefinitely from disk.
- Around line 240-249: The AppRole credential setup in vault-setup.sh writes
ROLE_ID and SECRET_ID before tightening permissions, leaving a window where
existing files or the APPROLE_DIR may be readable. Update the
APPROLE_DIR/role-id/secret-id handling so vault setup for osac-e2e creates and
secures the directory and files before any write of the SecretID, using the same
APPROLE_DIR, ROLE_ID, and SECRET_ID flow while ensuring restrictive permissions
are applied first.
🪄 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: osac-project/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 86c57386-5ca4-4c43-ab6d-f3c2e13b4a8f

📥 Commits

Reviewing files that changed from the base of the PR and between e4204af and fa4c44d.

📒 Files selected for processing (3)
  • scripts/machine-init.sh
  • vault/scripts/vault-migrate-secrets.sh
  • vault/scripts/vault-setup.sh

Comment thread scripts/machine-init.sh
Comment on lines +410 to +413
# Remove any pre-existing directory so the symlink can be created
rm -rf "${ROOT_CT_DIR}"
mkdir -p /root/.config
ln -sfn "${CT_CONFIG_DIR}" "${ROOT_CT_DIR}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Migrate existing root config before replacing it.

On reruns from the old root-based layout, Line 411 deletes /root/.config/cluster-tool before preserving state.json, keys, or server data. Copy the existing config into the runner-owned directory before replacing it with the symlink.

Proposed fix
-        # Remove any pre-existing directory so the symlink can be created
-        rm -rf "${ROOT_CT_DIR}"
+        # Preserve config from the old root-owned layout before replacing it.
+        if [[ -d "${ROOT_CT_DIR}" ]]; then
+            cp -a "${ROOT_CT_DIR}/." "${CT_CONFIG_DIR}/"
+            chown -R "${RUNNER_USER}:${RUNNER_USER}" "${CT_CONFIG_DIR}"
+        elif [[ -e "${ROOT_CT_DIR}" ]]; then
+            echo "ERROR: ${ROOT_CT_DIR} exists and is not a directory or symlink" >&2
+            return 1
+        fi
+        rm -rf "${ROOT_CT_DIR}"
         mkdir -p /root/.config
         ln -sfn "${CT_CONFIG_DIR}" "${ROOT_CT_DIR}"
🤖 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 `@scripts/machine-init.sh` around lines 410 - 413, The symlink setup in
machine-init.sh is deleting the existing root-owned cluster-tool config before
migrating its contents, which can lose state on reruns. Update the
initialization flow around the ROOT_CT_DIR replacement to first detect and copy
any existing config into the runner-owned CT_CONFIG_DIR, preserving state.json,
keys, and server data, and only then remove the old directory and create the
symlink. Keep the change localized to the config migration block that currently
runs rm -rf, mkdir -p /root/.config, and ln -sfn.

Comment thread vault/scripts/vault-setup.sh Outdated
Comment on lines +232 to +237
vault write auth/approle/role/osac-e2e \
token_policies="osac-e2e" \
token_ttl=10m \
token_max_ttl=30m \
secret_id_num_uses=0 \
secret_id_ttl=0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Bound the AppRole SecretID lifetime.

secret_id_num_uses=0 and secret_id_ttl=0 make the on-disk SecretID reusable indefinitely; a host read can keep minting tokens despite the short token TTL.

Proposed fix
+APPROLE_SECRET_ID_NUM_USES="${APPROLE_SECRET_ID_NUM_USES:-100}"
+APPROLE_SECRET_ID_TTL="${APPROLE_SECRET_ID_TTL:-24h}"
+
 vault write auth/approle/role/osac-e2e \
     token_policies="osac-e2e" \
     token_ttl=10m \
     token_max_ttl=30m \
-    secret_id_num_uses=0 \
-    secret_id_ttl=0
+    secret_id_num_uses="${APPROLE_SECRET_ID_NUM_USES}" \
+    secret_id_ttl="${APPROLE_SECRET_ID_TTL}"
📝 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
vault write auth/approle/role/osac-e2e \
token_policies="osac-e2e" \
token_ttl=10m \
token_max_ttl=30m \
secret_id_num_uses=0 \
secret_id_ttl=0
APPROLE_SECRET_ID_NUM_USES="${APPROLE_SECRET_ID_NUM_USES:-100}"
APPROLE_SECRET_ID_TTL="${APPROLE_SECRET_ID_TTL:-24h}"
vault write auth/approle/role/osac-e2e \
token_policies="osac-e2e" \
token_ttl=10m \
token_max_ttl=30m \
secret_id_num_uses="${APPROLE_SECRET_ID_NUM_USES}" \
secret_id_ttl="${APPROLE_SECRET_ID_TTL}"
🤖 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 `@vault/scripts/vault-setup.sh` around lines 232 - 237, The AppRole setup for
osac-e2e leaves the SecretID effectively unlimited because secret_id_num_uses
and secret_id_ttl are set to zero. Update the vault write
auth/approle/role/osac-e2e configuration to bound SecretID reuse and lifetime,
using the existing vault-setup.sh role definition so the SecretID can’t be
reused indefinitely from disk.

Comment thread vault/scripts/vault-setup.sh

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/machine-init.sh (1)

151-157: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validate the actual passwordless sudo contract.

A stale /etc/sudoers.d/${RUNNER_USER} can exist but not grant NOPASSWD, and verification still passes. Always install/validate the intended sudoers rule and verify with a non-interactive sudo check.

Proposed fix
-    if [[ ! -f "${SUDOERS_FILE}" ]]; then
-        echo "${RUNNER_USER} ALL=(ALL) NOPASSWD: ALL" > "${SUDOERS_FILE}"
-        chmod 0440 "${SUDOERS_FILE}"
-        echo "    Passwordless sudo configured."
-    fi
+    SUDOERS_TMP=$(mktemp)
+    printf '%s ALL=(ALL) NOPASSWD: ALL\n' "${RUNNER_USER}" > "${SUDOERS_TMP}"
+    visudo -cf "${SUDOERS_TMP}" >/dev/null
+    install -m 0440 "${SUDOERS_TMP}" "${SUDOERS_FILE}"
+    rm -f "${SUDOERS_TMP}"
+    echo "    Passwordless sudo configured."
-        if [[ ! -f "/etc/sudoers.d/${RUNNER_USER}" ]]; then
-            printf "  %-20s FAILED — sudoers file missing\n" "${RUNNER_USER}:"
+        if ! sudo -n -u "${RUNNER_USER}" sudo -n true 2>/dev/null; then
+            printf "  %-20s FAILED — passwordless sudo unavailable\n" "${RUNNER_USER}:"
             (( failures++ )) || true
         fi

Also applies to: 511-513

🤖 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 `@scripts/machine-init.sh` around lines 151 - 157, The passwordless sudo setup
in machine-init.sh only checks whether SUDOERS_FILE exists, so a stale sudoers
entry can bypass the intended NOPASSWD rule. Update the logic around the
RUNNER_USER sudoers file to always ensure the exact "ALL=(ALL) NOPASSWD: ALL"
rule is installed or replaced, then validate it with a non-interactive sudo
check before continuing. Use the existing SUDOERS_FILE and RUNNER_USER flow so
the fix applies consistently wherever this block is used.
♻️ Duplicate comments (1)
scripts/machine-init.sh (1)

362-365: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Reject unsafe DATA_PATH before privileged ownership changes.

DATA_PATH can come from ${CT_CONFIG_DIR}/config; if it is /, /etc, /root, or a symlinked system path, this root-run script creates/chowns host directories. Normalize and reject unsafe roots before mkdir/chown.

Proposed fix
     # Create data directories and ensure runner user owns them
+    DATA_PATH=$(realpath -m -- "${DATA_PATH}")
+    case "${DATA_PATH}" in
+        /|/etc|/etc/*|/usr|/usr/*|/bin|/bin/*|/sbin|/sbin/*|/boot|/boot/*|/root|/root/*)
+            echo "ERROR: Refusing unsafe DATA_PATH: ${DATA_PATH}" >&2
+            exit 1
+            ;;
+    esac
     mkdir -p "${DATA_PATH}/flavors" "${DATA_PATH}/overlays" "${DATA_PATH}/tmp" "${DATA_PATH}/containers/storage"
🤖 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 `@scripts/machine-init.sh` around lines 362 - 365, The data-directory setup in
machine-init.sh should validate DATA_PATH before the mkdir/chown block to avoid
changing ownership of unsafe host locations. Normalize the value loaded from
config, reject root or sensitive system paths like /, /etc, /root, and symlinked
targets, then only proceed with the existing directory creation and ownership
changes in the data path setup section. Use the DATA_PATH handling and the
create-data-directories/chown logic to place the safeguard immediately before
the privileged filesystem operations.
🤖 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 `@docs/new-ci-machine-setup.md`:
- Around line 141-165: The verification steps using cluster-tool are still
missing the prerequisite symlink/setup, so move the github-runner cluster-tool
config link before the “Verify” section and before any pull/boot/destroy
commands. Update the new-ci-machine-setup guide around the cluster-tool
instructions so /root/.config/cluster-tool points to the runner-owned config
before verification begins, using the existing cluster-tool
setup/troubleshooting steps as the reference point.
- Around line 55-61: The setup steps use a relative repo path that the
`github-runner` user cannot access after switching users, so update the
instructions around the `su - github-runner` and `vault-setup.sh` steps to use a
shared or absolute checkout location that the runner account can reach. Adjust
the earlier clone/check-out command and the subsequent `cd` in the setup flow so
they reference the same accessible path instead of a root-owned directory.

In `@scripts/machine-init.sh`:
- Around line 407-409: The symlink check in the machine-init flow is too
permissive: `ROOT_CT_DIR` is treated as valid whenever it exists as a link, even
if it is broken or points to an outdated target. Update the
`scripts/machine-init.sh` logic around the `ROOT_CT_DIR` handling to verify the
symlink target is still correct and repair/recreate it when it is stale, not
only when it is missing; keep the fix localized to the existing `if [[ -L
"${ROOT_CT_DIR}" ]]` block and related root config setup.

---

Outside diff comments:
In `@scripts/machine-init.sh`:
- Around line 151-157: The passwordless sudo setup in machine-init.sh only
checks whether SUDOERS_FILE exists, so a stale sudoers entry can bypass the
intended NOPASSWD rule. Update the logic around the RUNNER_USER sudoers file to
always ensure the exact "ALL=(ALL) NOPASSWD: ALL" rule is installed or replaced,
then validate it with a non-interactive sudo check before continuing. Use the
existing SUDOERS_FILE and RUNNER_USER flow so the fix applies consistently
wherever this block is used.

---

Duplicate comments:
In `@scripts/machine-init.sh`:
- Around line 362-365: The data-directory setup in machine-init.sh should
validate DATA_PATH before the mkdir/chown block to avoid changing ownership of
unsafe host locations. Normalize the value loaded from config, reject root or
sensitive system paths like /, /etc, /root, and symlinked targets, then only
proceed with the existing directory creation and ownership changes in the data
path setup section. Use the DATA_PATH handling and the
create-data-directories/chown logic to place the safeguard immediately before
the privileged filesystem operations.
🪄 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: osac-project/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 77e3d1b3-1825-4597-8ae1-8a91fd368556

📥 Commits

Reviewing files that changed from the base of the PR and between fa4c44d and 51179ac.

📒 Files selected for processing (5)
  • .github/workflows/e2e-full-install.yml
  • .github/workflows/e2e.yml
  • docs/new-ci-machine-setup.md
  • scripts/machine-init.sh
  • vault/scripts/vault-migrate-secrets.sh

Comment on lines +55 to +61
Switch to the `github-runner` user and run the Vault setup script:

```bash
su - github-runner
cd osac-test-infra
./vault/scripts/vault-setup.sh
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use a repo path the runner user can actually reach.

After su - github-runner, cd osac-test-infra will fail if the checkout was cloned as root in step 1, because that repo lives under /root. Move the checkout to a shared location or use an absolute path before switching users.

Suggested fix
-SSH into the new machine as root and clone the repo:
+SSH into the new machine and clone the repo into a shared location:
 
 ```bash
-git clone https://github.com/osac-project/osac-test-infra.git
-cd osac-test-infra
+git clone https://github.com/osac-project/osac-test-infra.git /opt/osac-test-infra
+cd /opt/osac-test-infra

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @docs/new-ci-machine-setup.md around lines 55 - 61, The setup steps use a
relative repo path that the github-runner user cannot access after switching
users, so update the instructions around the su - github-runner and
vault-setup.sh steps to use a shared or absolute checkout location that the
runner account can reach. Adjust the earlier clone/check-out command and the
subsequent cd in the setup flow so they reference the same accessible path
instead of a root-owned directory.


</details>

<!-- fingerprinting:phantom:triton:quartz -->

<!-- cr-indicator-types:potential_issue -->

<!-- cr-comment:v1:3d549036dbaa80696b3c5fec -->

<!-- This is an auto-generated comment by CodeRabbit -->

Comment on lines +141 to +165
As `github-runner`, pull the required cluster flavors:

```bash
su - github-runner
sudo python3 /usr/local/bin/cluster-tool pull quay.io/rh-ee-ovishlit/cluster-flavors:vmaas-helm
```

This creates the `state.json` file that the e2e workflow checks during its preflight.

### 8. Verify

```bash
# Check runner services are active
sudo systemctl status 'actions.runner.osac-project-*'

# Check runner logs
sudo journalctl -u 'actions.runner.osac-project-*' -f

# Verify in GitHub UI
# https://github.com/organizations/osac-project/settings/actions/runners

# Test a cluster boot
sudo python3 /usr/local/bin/cluster-tool boot --flavor vmaas-helm --name test-1
KUBECONFIG=~/.kube/test-1.kubeconfig oc get nodes
sudo python3 /usr/local/bin/cluster-tool destroy test-1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Move the cluster-tool symlink prerequisite before verification.

The sudo python3 /usr/local/bin/cluster-tool pull/boot checks in the verification section still need /root/.config/cluster-tool to point at the runner-owned config. Right now that setup only appears later in troubleshooting, so a fresh machine can fail before the reader reaches the fix.

Suggested fix
+sudo mkdir -p /root/.config
+sudo ln -sfn /home/github-runner/.config/cluster-tool /root/.config/cluster-tool

Also applies to: 184-195

🤖 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 `@docs/new-ci-machine-setup.md` around lines 141 - 165, The verification steps
using cluster-tool are still missing the prerequisite symlink/setup, so move the
github-runner cluster-tool config link before the “Verify” section and before
any pull/boot/destroy commands. Update the new-ci-machine-setup guide around the
cluster-tool instructions so /root/.config/cluster-tool points to the
runner-owned config before verification begins, using the existing cluster-tool
setup/troubleshooting steps as the reference point.

Comment thread scripts/machine-init.sh
Comment on lines +407 to +409
if [[ -L "${ROOT_CT_DIR}" ]]; then
echo " Symlink ${ROOT_CT_DIR} already exists."
else

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Repair stale root symlinks, not just missing ones.

Line 407 treats any symlink as valid. A broken or old /root/.config/cluster-tool symlink will keep workflows reading the wrong config path.

Proposed fix
     ROOT_CT_DIR="/root/.config/cluster-tool"
     if [[ -L "${ROOT_CT_DIR}" ]]; then
-        echo "    Symlink ${ROOT_CT_DIR} already exists."
+        CURRENT_TARGET=$(readlink -f -- "${ROOT_CT_DIR}" 2>/dev/null || true)
+        DESIRED_TARGET=$(readlink -f -- "${CT_CONFIG_DIR}")
+        if [[ "${CURRENT_TARGET}" == "${DESIRED_TARGET}" ]]; then
+            echo "    Symlink ${ROOT_CT_DIR} already exists."
+        else
+            ln -sfn "${CT_CONFIG_DIR}" "${ROOT_CT_DIR}"
+            echo "    Repaired symlink ${ROOT_CT_DIR} -> ${CT_CONFIG_DIR}"
+        fi
     else
📝 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 [[ -L "${ROOT_CT_DIR}" ]]; then
echo " Symlink ${ROOT_CT_DIR} already exists."
else
if [[ -L "${ROOT_CT_DIR}" ]]; then
CURRENT_TARGET=$(readlink -f -- "${ROOT_CT_DIR}" 2>/dev/null || true)
DESIRED_TARGET=$(readlink -f -- "${CT_CONFIG_DIR}")
if [[ "${CURRENT_TARGET}" == "${DESIRED_TARGET}" ]]; then
echo " Symlink ${ROOT_CT_DIR} already exists."
else
ln -sfn "${CT_CONFIG_DIR}" "${ROOT_CT_DIR}"
echo " Repaired symlink ${ROOT_CT_DIR} -> ${CT_CONFIG_DIR}"
fi
else
🤖 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 `@scripts/machine-init.sh` around lines 407 - 409, The symlink check in the
machine-init flow is too permissive: `ROOT_CT_DIR` is treated as valid whenever
it exists as a link, even if it is broken or points to an outdated target.
Update the `scripts/machine-init.sh` logic around the `ROOT_CT_DIR` handling to
verify the symlink target is still correct and repair/recreate it when it is
stale, not only when it is missing; keep the fix localized to the existing `if
[[ -L "${ROOT_CT_DIR}" ]]` block and related root config setup.

@eliorerz
eliorerz force-pushed the OSAC-1675-machine-init-fixes branch from c869647 to f2a3698 Compare July 2, 2026 22:42

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

🤖 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 @.github/workflows/e2e-vmaas.yml:
- Around line 243-245: The haproxy cleanup step in the e2e-vmaas workflow
mutates shared haproxy state without any serialization, which can race with
other concurrent jobs. Update the cleanup-haproxy invocation to run under the
same locking scheme used elsewhere in this job, such as FLAVOR_LOCK or a
dedicated haproxy lock, so it cannot overlap with boot or other config-writing
steps. Keep the existing cleanup-haproxy command but wrap it in the lock logic
used by the other shared-state mutations in this workflow.

In `@docs/new-ci-machine-setup.md`:
- Around line 77-83: The migration step is missing the required repo checkout
context for vault-migrate-secrets.sh, so update the instructions to explicitly
tell the reader to run it from the repository root (or use the absolute path to
the checked-out script). Keep the example around
vault/scripts/vault-migrate-secrets.sh but make the needed checkout location
clear so the source-host and new-host commands work regardless of the current
directory.
🪄 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: osac-project/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: c8160a1e-d4d9-4213-b0ed-bd6792808676

📥 Commits

Reviewing files that changed from the base of the PR and between 51179ac and f2a3698.

📒 Files selected for processing (5)
  • .github/workflows/e2e-vmaas.yml
  • docs/new-ci-machine-setup.md
  • scripts/machine-init.sh
  • vault/scripts/vault-migrate-secrets.sh
  • vault/scripts/vault-setup.sh

Comment on lines +243 to +245
# Remove haproxy entries for VMs that no longer exist
sudo python3 /usr/local/bin/cluster-tool cleanup-haproxy 2>/dev/null || true

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Unguarded haproxy cleanup races with concurrent jobs on the shared runner.

This step mutates the shared /etc/haproxy/haproxy.cfg outside of any flock, while every other shared-state mutation in this same job (flavor pull/delete/boot) is deliberately guarded by FLAVOR_LOCK to serialize concurrent jobs on osac-ci. If cleanup-haproxy runs concurrently with another job's boot (which also touches haproxy config for its own clone), the cleanup could race with that write — removing/corrupting a backend entry for a VM that's mid-creation, or clobbering config while another process is writing it.

Consider serializing this with the existing lock (or a dedicated haproxy lock) rather than leaving it unguarded:

🔒 Suggested fix
-          # Remove haproxy entries for VMs that no longer exist
-          sudo python3 /usr/local/bin/cluster-tool cleanup-haproxy 2>/dev/null || true
+          # Remove haproxy entries for VMs that no longer exist. Serialize with
+          # other jobs since this mutates the shared haproxy config.
+          exec 9>"${FLAVOR_LOCK}"
+          flock --exclusive 9
+          sudo python3 /usr/local/bin/cluster-tool cleanup-haproxy 9>&- 2>/dev/null || true
+          exec 9>&-
📝 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
# Remove haproxy entries for VMs that no longer exist
sudo python3 /usr/local/bin/cluster-tool cleanup-haproxy 2>/dev/null || true
# Remove haproxy entries for VMs that no longer exist. Serialize with
# other jobs since this mutates the shared haproxy config.
exec 9>"${FLAVOR_LOCK}"
flock --exclusive 9
sudo python3 /usr/local/bin/cluster-tool cleanup-haproxy 9>&- 2>/dev/null || true
exec 9>&-
🤖 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 @.github/workflows/e2e-vmaas.yml around lines 243 - 245, The haproxy cleanup
step in the e2e-vmaas workflow mutates shared haproxy state without any
serialization, which can race with other concurrent jobs. Update the
cleanup-haproxy invocation to run under the same locking scheme used elsewhere
in this job, such as FLAVOR_LOCK or a dedicated haproxy lock, so it cannot
overlap with boot or other config-writing steps. Keep the existing
cleanup-haproxy command but wrap it in the lock logic used by the other
shared-state mutations in this workflow.

Comment on lines +77 to +83
Run from a machine that can SSH into both the source and destination hosts:

```bash
./vault/scripts/vault-migrate-secrets.sh \
github-runner@<source-host> /path/to/source-vault-init.json \
github-runner@<new-host> /path/to/new-vault-init.json
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Call out the required checkout location for the migration script.

./vault/scripts/vault-migrate-secrets.sh only works from this repo checkout, but the step never says to cd into it or use an absolute path. As written, this will fail on any host that is not already in the cloned tree.

🤖 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 `@docs/new-ci-machine-setup.md` around lines 77 - 83, The migration step is
missing the required repo checkout context for vault-migrate-secrets.sh, so
update the instructions to explicitly tell the reader to run it from the
repository root (or use the absolute path to the checked-out script). Keep the
example around vault/scripts/vault-migrate-secrets.sh but make the needed
checkout location clear so the source-host and new-host commands work regardless
of the current directory.

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

/lgtm
/approve

I'll just say that an Ansible playbook would probably have been simpler than shell scripts

@openshift-ci

openshift-ci Bot commented Jul 3, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: eliorerz, ygalblum

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

eliorerz added 6 commits July 3, 2026 12:14
…ault migrate script

- Add runner-user step to machine-init.sh: creates github-runner user
  with libvirt group, passwordless sudo, and SSH authorized_keys
- Fix cluster-tool config to target runner user's home instead of root's,
  resolving "No server specified" error when running as github-runner
- Fix vault-setup.sh rootless Podman chown using podman unshare
- Add vault-migrate-secrets.sh for copying KV v2 secrets between Vaults
Chown ~/.config itself (not just subdirs) so podman doesn't reject
the parent directory when running as github-runner.
The e2e workflow uses AppRole (role-id + secret-id) to authenticate
with Vault. Add phase 10 to enable AppRole auth, create the osac-e2e
role, and write credentials to ~/.vault-server/.approle/.
The e2e workflow runs cluster-tool via sudo, which looks for config
under /root/.config/cluster-tool/. Symlink it to the runner user's
config so both sudo and non-sudo invocations work.
Remove orphaned haproxy entries (for VMs that no longer exist)
before each boot to prevent config drift from killed/crashed jobs.
Set OOMScoreAdjust=-1000 on sshd so the machine stays reachable
via SSH even when VMs or runners exhaust memory.
@eliorerz
eliorerz force-pushed the OSAC-1675-machine-init-fixes branch from f2a3698 to aedf907 Compare July 3, 2026 16:15
@openshift-ci openshift-ci Bot removed the lgtm label Jul 3, 2026
@openshift-ci

openshift-ci Bot commented Jul 3, 2026

Copy link
Copy Markdown

New changes are detected. LGTM label has been removed.

@eliorerz

eliorerz commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

/retest

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown

No failed workflow runs found for this PR at commit aedf907.

@openshift-merge-bot
openshift-merge-bot Bot merged commit 83ea232 into osac-project:main Jul 4, 2026
7 checks passed
@eliorerz
eliorerz deleted the OSAC-1675-machine-init-fixes branch July 4, 2026 19:23

This branch was previously deployed

1 inactive deployment
e2e-test — aedf9079 Deployed Jul 3, 2026 by eliorerz via e2e-vmaas-full-install / e2e #131
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants