From 9d398fb874f95c5034c280520b00ac6a674c1954 Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Wed, 1 Apr 2026 17:30:24 +1300 Subject: [PATCH 1/4] agent: Add download-logs mode to UI installer Add a --download-logs command-line flag to the UI-driven cluster installation tool that downloads installation logs from the Assisted Installer UI and exits. When invoked with --download-logs, the tool: - Launches a headless browser and connects to the Assisted Installer - Searches for the "Download installation logs" button - Downloads the logs by clicking the button - Saves the logs to OCP_DIR/installation-logs.tar This operates independently of the normal installation flow and can be called as a separate command. Logs are saved alongside screenshots in OCP_DIR for later archival. The download is best-effort - if the button is not visible or the UI is not accessible, the tool logs a warning and exits successfully. Assisted-by: Claude Code --- .../ui_driven_cluster_installation/main.go | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/agent/isobuilder/ui_driven_cluster_installation/main.go b/agent/isobuilder/ui_driven_cluster_installation/main.go index 1ade00eca..9a1c8bb7b 100644 --- a/agent/isobuilder/ui_driven_cluster_installation/main.go +++ b/agent/isobuilder/ui_driven_cluster_installation/main.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "flag" "fmt" "log" "net/http" @@ -26,6 +27,7 @@ import ( ) var ( + downloadLogsFlag = flag.Bool("download-logs", false, "Download installation logs from UI and exit") clusterName = os.Getenv("CLUSTER_NAME") baseDomain = os.Getenv("BASE_DOMAIN") rendezvousIP = os.Getenv("RENDEZVOUS_IP") @@ -41,6 +43,16 @@ var ( ) func main() { + // Parse command-line flags + flag.Parse() + + // Check if download-logs mode requested + if *downloadLogsFlag { + runDownloadLogs() + return + } + + // Default mode: perform installation logrus.Info("Launching headless browser...") logConfiguration() @@ -462,3 +474,84 @@ func saveCredentials(client *resty.Client, url, filename string) error { } return errs.Errorf("%s was not downloaded after %d attempts", filename, downloadAttempts) } + +// downloadInstallationLogs downloads installation logs by clicking the download button +func downloadInstallationLogs(browser *rod.Browser, page *rod.Page) error { + logrus.Info("Attempting to download installation logs") + + // Check if ocpDir is set + if ocpDir == "" { + logrus.Warn("OCP_DIR not set, cannot download installation logs") + return errors.New("OCP_DIR environment variable not set") + } + + // OCP_DIR should already exist (created during installation) + // But ensure it exists just in case + if err := os.MkdirAll(ocpDir, 0755); err != nil { + return fmt.Errorf("failed to create OCP directory %s: %w", ocpDir, err) + } + + // Find the download button - case-insensitive search + downloadBtn, err := page.Timeout(10*time.Second).ElementR("button", "(?i)Download installation logs") + if err != nil { + logrus.Warnf("Could not find download logs button: %v", err) + return err + } + + if visible, _ := downloadBtn.Visible(); !visible { + logrus.Warn("Download logs button not visible") + return errors.New("download logs button not visible") + } + + // Set up download handler before clicking + wait := browser.MustWaitDownload() + + // Click the download button + logrus.Info("Clicking download logs button") + downloadBtn.MustClick() + + // Wait for download to complete and get the downloaded bytes + downloadData := wait() + + // Save to OCP_DIR alongside screenshots + logFile := filepath.Join(ocpDir, "installation-logs.tar") + logrus.Infof("Saving installation logs to %s", logFile) + + err = os.WriteFile(logFile, downloadData, 0644) + if err != nil { + return fmt.Errorf("failed to save logs to %s: %w", logFile, err) + } + + logrus.Infof("Installation logs saved successfully to %s", logFile) + return nil +} + +// runDownloadLogs connects to the UI and downloads installation logs if available +func runDownloadLogs() { + logrus.Info("Running in download-logs mode") + + // Launch browser + chromiumPath, _ := launcher.LookPath() + url := launcher.New().Bin(chromiumPath).NoSandbox(true).Headless(true).MustLaunch() + browser := rod.New().ControlURL(url).MustConnect() + defer browser.MustClose() + + // Navigate to base URL - UI will redirect to current cluster + page := browser.MustPage(baseURL) + page.MustWaitLoad() + + logrus.Info("Connected to Assisted Installer UI") + + // The download button is only available on certain pages (e.g., Installation Progress) + // The page may be on cluster details, progress, or completion page + // Best effort: try to download logs from wherever we are + if err := downloadInstallationLogs(browser, page); err != nil { + logrus.Warnf("Could not download installation logs: %v", err) + // Not a fatal error - button may not be visible on current page + // or logs may not be ready yet + } else { + logrus.Info("Installation logs downloaded successfully") + } + + logrus.Info("Download-logs mode complete") +} From 128412f336541fc9792615b1f2477daaad210ca7 Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Wed, 1 Apr 2026 17:39:42 +1300 Subject: [PATCH 2/4] agent: Fix shellcheck warnings in gather.sh Fix pre-existing shellcheck warnings in agent/gather.sh: - SC2162: Add -r flag to read command to prevent backslash mangling - SC2027/SC2086: Fix quoting in echo string (line 15) - SC2004: Remove unnecessary $ in arithmetic expansion All changes are style/correctness improvements with no functional impact. Assisted-by: Claude Code --- agent/gather.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/agent/gather.sh b/agent/gather.sh index 201cc890e..88214fab6 100755 --- a/agent/gather.sh +++ b/agent/gather.sh @@ -6,13 +6,13 @@ SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )" source "$SCRIPTDIR"/common.sh -while read line +while read -r line do ip=$( echo "$line" | cut -d " " -f 1) host=$( echo "$line" | cut -d " " -f 2) echo "Trying to gather agent logs on host ${host}" if ssh -n -o 'ConnectTimeout=30' -o 'StrictHostKeyChecking=no' -o 'UserKnownHostsFile=/dev/null' core@"${ip}" agent-gather -O >agent-gather-"${host}".tar.xz; then - echo "Agent logs saved to agent-gather-"${host}".tar.xz" >&2 + echo "Agent logs saved to agent-gather-${host}.tar.xz" >&2 else if [ $? == 127 ]; then echo "Skipping gathering agent logs, agent-gather script not present on host ${host}." >&2 @@ -23,14 +23,14 @@ done < "${OCP_DIR}"/hosts num_tui_screenshots=$(find "${OCP_DIR}" -type f -name "*.ppm" | wc -l) num_ui_screenshots=$(find "${OCP_DIR}" -type f -name "*.png" | wc -l) -num_screenshots=$(($num_tui_screenshots + $num_ui_screenshots)) +num_screenshots=$((num_tui_screenshots + num_ui_screenshots)) if [[ "$num_screenshots" -gt 0 ]]; then archive_name="agent-gather-console-screenshots.tar.xz" echo "Gathering screenshots to $archive_name" if [[ "${AGENT_E2E_TEST_BOOT_MODE}" == "ISO_NO_REGISTRY" ]] && compgen -G "${OCP_DIR}/*.png" > /dev/null; then - tar -cJf $archive_name ${OCP_DIR}/*.ppm ${OCP_DIR}/*.png + tar -cJf $archive_name "${OCP_DIR}"/*.ppm "${OCP_DIR}"/*.png else - tar -cJf $archive_name ${OCP_DIR}/*.ppm + tar -cJf $archive_name "${OCP_DIR}"/*.ppm fi else echo "No screenshots found. Skipping screenshot gather." From 017090093402003b232bd2703433ee6df2226e9d Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Wed, 1 Apr 2026 17:31:19 +1300 Subject: [PATCH 3/4] agent: Refactor gather.sh to build file list for archiving Replace multiple conditional tar commands with a single command that builds a file list first. This simplifies the code by eliminating four different tar command combinations while maintaining identical behavior. The refactoring: - Creates files_to_archive array - Conditionally adds .ppm and .png files to the array - Creates the archive with a single tar command This makes it easier to add additional file types to the archive in the future without adding more conditional branches. Assisted-by: Claude Code --- agent/gather.sh | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/agent/gather.sh b/agent/gather.sh index 88214fab6..95f176e7b 100755 --- a/agent/gather.sh +++ b/agent/gather.sh @@ -27,11 +27,20 @@ num_screenshots=$((num_tui_screenshots + num_ui_screenshots)) if [[ "$num_screenshots" -gt 0 ]]; then archive_name="agent-gather-console-screenshots.tar.xz" echo "Gathering screenshots to $archive_name" + + # Build list of files to archive + files_to_archive=() + + # Always include TUI screenshots + files_to_archive+=("${OCP_DIR}"/*.ppm) + + # Include UI screenshots if in ISO_NO_REGISTRY mode if [[ "${AGENT_E2E_TEST_BOOT_MODE}" == "ISO_NO_REGISTRY" ]] && compgen -G "${OCP_DIR}/*.png" > /dev/null; then - tar -cJf $archive_name "${OCP_DIR}"/*.ppm "${OCP_DIR}"/*.png - else - tar -cJf $archive_name "${OCP_DIR}"/*.ppm + files_to_archive+=("${OCP_DIR}"/*.png) fi + + # Create archive with all collected files + tar -cJf $archive_name "${files_to_archive[@]}" else echo "No screenshots found. Skipping screenshot gather." fi From 15942d4142b6f88196b8b9b97905d23fc737c0cf Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Wed, 1 Apr 2026 17:31:53 +1300 Subject: [PATCH 4/4] agent: Download and archive installation logs in gather.sh Integrate the --download-logs mode into the artifact collection process. When running in ISO_NO_REGISTRY boot mode, gather.sh now: - Invokes the UI installer with --download-logs flag - Includes the downloaded installation-logs.tar in the screenshot archive if available The log download is best-effort and uses || true to ensure gather.sh continues successfully even if the download fails (e.g., if the UI is not accessible or logs are not yet available). Installation logs are now collected alongside screenshots in the agent-gather-console-screenshots.tar.xz archive, providing a complete set of console artifacts for debugging. Assisted-by: Claude Code --- agent/gather.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/agent/gather.sh b/agent/gather.sh index 95f176e7b..2dfc0207f 100755 --- a/agent/gather.sh +++ b/agent/gather.sh @@ -5,6 +5,7 @@ set -euxo pipefail SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )" source "$SCRIPTDIR"/common.sh +source "$SCRIPTDIR"/agent/common.sh while read -r line do @@ -28,6 +29,17 @@ if [[ "$num_screenshots" -gt 0 ]]; then archive_name="agent-gather-console-screenshots.tar.xz" echo "Gathering screenshots to $archive_name" + # Attempt to download installation logs from the UI + if [[ "${AGENT_E2E_TEST_BOOT_MODE}" == "ISO_NO_REGISTRY" ]]; then + echo "Attempting to download installation logs from Assisted Installer UI" + rendezvousIP=$(getRendezvousIP) + ocp_dir_abs_path="$(realpath "${OCP_DIR}")" + + pushd agent/isobuilder/ui_driven_cluster_installation + RENDEZVOUS_IP=$rendezvousIP OCP_DIR=$ocp_dir_abs_path go run main.go --download-logs || true + popd + fi + # Build list of files to archive files_to_archive=() @@ -39,6 +51,11 @@ if [[ "$num_screenshots" -gt 0 ]]; then files_to_archive+=("${OCP_DIR}"/*.png) fi + # Include installation logs if available + if [[ -f "${OCP_DIR}/installation-logs.tar" ]]; then + files_to_archive+=("${OCP_DIR}/installation-logs.tar") + fi + # Create archive with all collected files tar -cJf $archive_name "${files_to_archive[@]}" else