install_functions.sh #2

  • //
  • guest/
  • perforce_software/
  • command-runner/
  • scripts/
  • install_functions.sh
  • View
  • Commits
  • Open Download .zip Download (12 KB)
#!/bin/bash

# Custom Helper Functions for Logging/Errors
WarningCount=0
ErrorCount=0

function msg() {
  if [ "$DEBUG_MODE" = "true" ]; then
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $*" >&2
  fi
  echo " - $*"
}

# Function to print debug messages only if DEBUG_MODE is enabled
debug_msg() {
  if [ "$DEBUG_MODE" = "true" ]; then
    echo "DEBUG: $(date '+%Y-%m-%d %H:%M:%S') - $*" >&2
  fi
}

function warnmsg() {
  msg "Warning: ${1:-Unknown Warning}" >&2
  WarningCount=$((WarningCount + 1))
}

function errmsg() {
  msg "Error: ${1:-Unknown Error}" >&2
  (( ErrorCount++ ))
}

function bail() {
  errmsg "${1:-Unknown Error}"
  exit "${2:-1}"
}

# run($cmd, $desc) - Executes a command with an optional description.
function run {
  local cmd="$1"
  local desc="${2:-}"
  [[ -n "$desc" ]] && msg "$desc"
  msg "Running: $cmd"
  if [ "$DRY_RUN" = "true" ]; then
    msg "[DRY RUN] (Not actually running '$cmd')"
    return 0
  fi
  eval "$cmd"
}

# Function to detect instances from systemd service
detect_instance_from_service() {
    local exec_line
    local detected_instance

    # Check if the service exists before attempting to extract the instance
    if ! sudo systemctl cat command-runner.service --no-pager -l &>/dev/null; then
        debug_msg "command-runner.service not found. Skipping instance detection from systemd." >&2
        return 1
    fi

    # Extract the ExecStart line from systemd
    exec_line=$(sudo systemctl cat command-runner.service --no-pager -l | grep ExecStart= | head -n 1 || true)

    if [[ -n "$exec_line" ]]; then
        debug_msg "Found command-runner service ExecStart: $exec_line" >&2

        # Extract --instance=<value> from the ExecStart line (portable sed instead of grep -oP)
        detected_instance=$(echo "$exec_line" | sed -n 's/.*--instance=\([^ ]*\).*/\1/p' | head -n 1)

        if [[ -n "$detected_instance" ]]; then
            echo "$detected_instance"  # Output only the instance name
            return 0
        fi
    fi

    return 1
}

# Function to detect instances based on /p4/<instance>/root
detect_instances_sdp_structure() {
  local instances=()
  local instance_name root_dir resolved_root
  debug_msg "Starting instance detection in $DEFAULT_BASE_DIR" >&2
  if [ ! -d "$DEFAULT_BASE_DIR" ]; then
    debug_msg "No /p4 directory found. Skipping instance detection." >&2
    return 0
  fi

  for dir in "$DEFAULT_BASE_DIR"/*/; do
    if [ ! -d "$dir" ]; then
      debug_msg "No directories found in $DEFAULT_BASE_DIR" >&2
      break
    fi

    instance_name="$(basename "$dir")"
    # Skip known non-instance directories
    if [[ "$instance_name" = "common" ]] || [[ "$instance_name" = "sdp" ]] || [[ "$instance_name" = "ssl" ]]; then
      debug_msg "Skipping '$instance_name' directory." >&2
      continue
    fi
    
    root_dir="$dir/root"
    if [ -L "$root_dir" ]; then
      resolved_root="$(readlink -f "$root_dir")"
      if [ -d "$resolved_root" ]; then
        debug_msg "Detected instance '$instance_name' with root at '$resolved_root'" >&2
        instances+=("$instance_name")
      else
        warnmsg "'$root_dir' symlink -> '$resolved_root' is not a directory."
      fi
    elif [ -d "$root_dir" ]; then
      debug_msg "Detected instance '$instance_name' with root at '$root_dir'" >&2
      instances+=("$instance_name")
    else
      warnmsg "'root' not found for '$instance_name'"
    fi
  done

  if [ "${#instances[@]}" -eq 0 ]; then
    debug_msg "No instances found." >&2
  else
    debug_msg "Detected instances: ${instances[*]}" >&2
    echo "${instances[@]}"
  fi
}
verify_instance() {
  if [ "$SDP_MODE" = "false" ]; then
    msg "Using non-P4 defaults..."
    return 0
  fi

  msg "Using P4 SDP instance detection..."
  
  # If instance is already set, do nothing
  if [ -n "${INSTANCE:-}" ]; then
    return 0
  fi

  # Try detecting instances using SDP structure
  mapfile -t instances < <(detect_instances_sdp_structure)
  INSTANCE_COUNT=${#instances[@]}

  if [ "$INSTANCE_COUNT" -eq 0 ]; then
    msg "Error: No valid instances found in $DEFAULT_BASE_DIR." >&2
    return 1
  elif [ "$INSTANCE_COUNT" -eq 1 ]; then
    INSTANCE="${instances[0]}"
    msg "Detected single instance: $INSTANCE"
  else
    if [ "$FORCE_INSTALL" = "true" ]; then
      msg "Error: Multiple instances found. Please specify one with -i <instance>." >&2
      exit 1
    fi

    msg "Multiple valid instances found. Please select one:"
    select selected_instance in "${instances[@]}" "Cancel"; do
      if [[ "$selected_instance" == "Cancel" ]]; then
        msg "Installation canceled."
        exit 1
      elif [[ " ${instances[*]} " == *" $selected_instance "* ]]; then
        INSTANCE="$selected_instance"
        break
      else
        msg "Invalid selection."
      fi
    done
  fi
}

# Function to confirm installation
function confirm_proceed() {
  if [ "$FORCE_INSTALL" = "true" ]; then
    msg "Force install selected: skipping installation confirmation prompt."
    return
  fi

  read -rp "Do you want to proceed with the installation? [y/N]: " confirmation
  case "$confirmation" in
    [Yy]|[Yy][Ee][Ss]) msg "Proceeding with installation..." ;;
    *) msg "Installation aborted by user."; exit 0 ;;
  esac
}

# Function to display summary and confirm
function prompt_summary_and_confirm() {
  if [ "$DRY_RUN" = "false" ]; then
    display_summary
    confirm_proceed
  else
    msg "DRY RUN MODE: No changes will be applied."
    display_summary
    msg "Re-run with -y to apply changes."
  fi
}

# Function to display binary version
function get_bin_version() {
  if [[ -z "${NEW_BINARY:-}" ]]; then
    msg "NEW_BINARY is not set; cannot determine new binary version."
    NEW_VERSION=""
    return 0
  fi

  if [[ ! -x "$NEW_BINARY" ]]; then
    msg "New binary '$NEW_BINARY' is not executable or not found; skipping new version detection."
    NEW_VERSION=""
    return 0
  fi

  new_raw="$($NEW_BINARY --version 2>/dev/null | sed '/^[[:space:]]*$/d' | head -n1 || true)"
  debug_msg "Raw version output from new binary: $new_raw"
  if [[ -z "$new_raw" ]]; then
    msg "No version info from '$NEW_BINARY'."
    NEW_VERSION=""
  else
    parsed_new="$(echo "$new_raw" | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+')"
    if [[ -n "$parsed_new" ]]; then
      NEW_VERSION="$parsed_new"
      debug_msg "Parsed new version: $NEW_VERSION"
    else
      msg "Unable to parse semantic version from '$NEW_BINARY' output."
      NEW_VERSION=""
    fi
  fi
}
# Function to check if a user exists
function user_exists() {
  local user="$1"
  [[ -z "$user" ]] && { errmsg "user_exists: no user specified"; return 1; }
  
  if id "$user" &>/dev/null; then
    debug_msg "User '$user' exists."
    return 0
  else
    errmsg "User '$user' does not exist."
    return 1
  fi
}

# Function to check if a group exists
function group_exists() {
  local group="$1"
  [[ -z "$group" ]] && { errmsg "group_exists: no group specified"; return 1; }
  
  if getent group "$group" &>/dev/null; then
    debug_msg "Group '$group' exists."
    return 0
  else
    errmsg "Group '$group' does not exist."
    return 1
  fi
}

# Function to check both user and group
function user_group_exists() {
  local user="$1"
  local group="$2"

  user_exists "$user" || return 1
  group_exists "$group" || return 1

  msg "User '$user' and group '$group' both exist."
  return 0
}
# Function to display the installation summary
function display_summary() {
  cat <<EOF

====================== Installation Summary ======================

Instance:                      $INSTANCE
Binary Install Path:           $INSTALL_PATH
Configuration Directory:       $CONFIG_DIR
VMAgent Directory:             ${VMAGENT_DIR:-/var/vmagent}
Log File:                      $LOG_FILE
Service Runs As:               $SVC_USER:$SVC_GROUP
Timer Mode:                    $( [ "$TIMER_MODE" = "test" ] && echo "TEST (Runs every 5 minutes)" || echo "PRODUCTION (Runs daily at 23:00)" )

New Binary Version:            $NEW_VERSION
Existing Version:              ${CURRENT_VERSION:-None}
Force Install:                 $( [ "$FORCE_INSTALL" = "true" ] && echo "Yes" || echo "No" )

SELinux Mode:                  ${SELinuxMode:-Unknown}

===============================================================

EOF
}

# Function to display installation completion message
function installation_complete() {
  msg "==================================================="
  if [ "$DRY_RUN" = "true" ]; then
    msg "DRY RUN COMPLETE (no changes made)."
    msg "Re-run with -y to apply changes."
  else
    msg "INSTALLATION COMPLETE!"
  fi
  msg "==================================================="
  msg "Owner:                  $OWNER"
  msg "New binary version:     $NEW_VERSION"
  msg "Previously installed:   ${CURRENT_VERSION:-None}"
  
  msg "Installed to:           $INSTALL_PATH"
  msg "Config installed to:    $CONFIG_DIR/cmd_config.yaml"
  msg "Log file:               $LOG_FILE"
  msg "Instance:               $INSTANCE"
  msg "VMAgent Directory:      ${VMAGENT_DIR:-/var/vmagent}"
  msg "Old cron references:    $OLD_SCRIPTNAME"
  msg "Service runs as:        $SVC_USER:$SVC_GROUP"
  
  if [ "$TIMER_MODE" = "test" ]; then
    msg "Timer Mode:             TEST (Runs every 5 minutes)"
  else
    msg "Timer Mode:             PRODUCTION (Runs daily at 23:00)"
  fi
  
  msg ""
  msg "Systemd service:        /etc/systemd/system/$SERVICE_NAME"
  msg "Systemd timer:          /etc/systemd/system/$TIMER_NAME"
  
  if [ "$TIMER_MODE" = "test" ]; then
    msg "Runs every 5 minutes   (OnBootSec=5m, OnUnitActiveSec=5m)"
  else
    msg "Runs daily at 23:00     (OnCalendar=*-*-* 23:00:00)"
  fi
  
  msg "Check status:           systemctl status $TIMER_NAME"
  msg ""
  msg "==================================================="
}

print_args_box() {
  if [ "$DEBUG_MODE" = "true" ]; then
    local ARGS="$*"
    local ARG_LENGTH=${#ARGS}
    local WIDTH=$((ARG_LENGTH + 10))  # Adjust width for spacing

    # Create the top border
    printf '=%.0s' $(seq 1 $WIDTH)
    echo ""

    # Create the empty line with side borders
    printf "|%*s|\n" "$WIDTH" " "

    # Print the ARGS line, centered
    printf "| ARGS %s%*s |\n" "$ARGS" $((WIDTH - ARG_LENGTH - 6)) " "

    # Create another empty line with side borders
    printf "|%*s|\n" "$WIDTH" " "

    # Create the bottom border
    printf '=%.0s' $(seq 1 $WIDTH)
    echo ""
    echo "DEBUG: $(date '+%Y-%m-%d %H:%M:%S') - $*" >&2
  fi
}

update_sdp_instance_yaml() {
    local config_file="$CONFIG_DIR/cmd_config.yaml"
    local backup_file="${config_file}.bak"

    debug_msg "Updating SDP instance in: $config_file"

    # Ensure the config file exists before proceeding
    if [ ! -f "$config_file" ]; then
        errmsg "Configuration file not found at $config_file"
        return 1
    fi

    # Create a backup before modifying (portable approach)
    debug_msg "Creating backup at: $backup_file"
    if ! cp "$config_file" "$backup_file"; then
        errmsg "Failed to create backup of $config_file"
        return 1
    fi

    # Perform the replacement of the entire line for sdp_instance
    debug_msg "Replacing 'sdp_instance' with: $INSTANCE"
    if ! sed -i.tmp "s|^sdp_instance:.*|sdp_instance: \"$INSTANCE\"|g" "$config_file"; then
        errmsg "Failed to update sdp_instance in $config_file"
        cp "$backup_file" "$config_file"  # Restore from backup
        return 1
    fi
    rm -f "${config_file}.tmp"  # Remove sed backup file

    if [ "$SDP_MODE" = "false" ]; then
        # More robust replacement of `p4: true` → `p4: false` inside `enable_commands`
        debug_msg "Setting 'p4' to false in $config_file"
        if ! awk '
            /enable_commands:/ { inside_block=1 }
            inside_block && /p4: true/ { sub(/p4: true/, "p4: false") }
            inside_block && /^[^ ]/ { inside_block=0 }  # Stop if we leave indentation level
            { print }
        ' "$config_file" > "${config_file}.tmp"; then
            errmsg "Failed to modify p4 setting in $config_file"
            cp "$backup_file" "$config_file"  # Restore from backup
            return 1
        fi
        mv "${config_file}.tmp" "$config_file"
    else
        debug_msg "SDP Mode is enabled. No additional modifications."
    fi

    # Verify replacements
    if grep -q "sdp_instance: \"$INSTANCE\"" "$config_file" && \
       ( [ "$SDP_MODE" = "true" ] || grep -q "  p4: false" "$config_file" ); then
        debug_msg "SDP instance successfully updated to: $INSTANCE"
        return 0
    else
        errmsg "Failed to update SDP instance or 'p4' flag in $config_file"
        cp "$backup_file" "$config_file"  # Restore from backup
        return 1
    fi
}



# Change User Description Committed
#2 32929 Robert Cowham New version of binary v2.0.0
#1 32922 Robert Cowham New version of command-runner