mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-22 03:02:35 +00:00
Move service stop to happen before attempting to copy the binary file. This prevents the error when the binary is still in use by the running service. Added 2-second delay to ensure process fully releases the file.
469 lines
No EOL
14 KiB
Bash
Executable file
469 lines
No EOL
14 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
# Pulse Installer Script for Go Version
|
|
# Supports: Ubuntu 20.04+, Debian 11+, Proxmox VE 7+
|
|
|
|
set -euo pipefail
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Configuration
|
|
INSTALL_DIR="/opt/pulse"
|
|
CONFIG_DIR="/etc/pulse" # All config and data goes here for manual installs
|
|
SERVICE_NAME="pulse"
|
|
GITHUB_REPO="rcourtman/Pulse"
|
|
REQUIRED_GO_VERSION="1.21"
|
|
|
|
# Detect existing service name (pulse or pulse-backend)
|
|
detect_service_name() {
|
|
if systemctl list-unit-files --no-legend | grep -q "^pulse-backend.service"; then
|
|
echo "pulse-backend"
|
|
elif systemctl list-unit-files --no-legend | grep -q "^pulse.service"; then
|
|
echo "pulse"
|
|
else
|
|
echo "pulse" # Default for new installations
|
|
fi
|
|
}
|
|
|
|
# Functions
|
|
print_header() {
|
|
echo -e "${BLUE}=================================================${NC}"
|
|
echo -e "${BLUE} Pulse Installation Script${NC}"
|
|
echo -e "${BLUE}=================================================${NC}"
|
|
echo
|
|
}
|
|
|
|
print_error() {
|
|
echo -e "${RED}[ERROR] $1${NC}" >&2
|
|
}
|
|
|
|
print_success() {
|
|
echo -e "${GREEN}[SUCCESS] $1${NC}"
|
|
}
|
|
|
|
print_info() {
|
|
echo -e "${YELLOW}[INFO] $1${NC}"
|
|
}
|
|
|
|
check_root() {
|
|
if [[ $EUID -ne 0 ]]; then
|
|
print_error "This script must be run as root"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
check_pre_v4_installation() {
|
|
# Check for Node.js version indicators (pre-v4 used Node.js)
|
|
# Note: pulse-backend.service is NOT a reliable indicator as v4 can use it too
|
|
if [[ -f "$INSTALL_DIR/.env" ]] || \
|
|
[[ -d "$INSTALL_DIR/node_modules" ]] || \
|
|
[[ -f "$INSTALL_DIR/package.json" ]] || \
|
|
[[ -f "$INSTALL_DIR/server.js" ]] || \
|
|
[[ -d "$INSTALL_DIR/backend" ]] || \
|
|
[[ -d "$INSTALL_DIR/frontend" ]]; then
|
|
|
|
echo
|
|
print_error "Pre-v4 Pulse Installation Detected"
|
|
echo
|
|
echo -e "${BLUE}=========================================================${NC}"
|
|
echo -e "${YELLOW}Pulse v4 is a complete rewrite that requires migration${NC}"
|
|
echo -e "${BLUE}=========================================================${NC}"
|
|
echo
|
|
echo "Your current installation appears to be a pre-v4 version."
|
|
echo "Due to fundamental architecture changes, automatic upgrade is not supported."
|
|
echo
|
|
echo -e "${GREEN}Recommended approach:${NC}"
|
|
echo "1. Create a fresh LXC container or VM"
|
|
echo "2. Install Pulse v4 in the new environment"
|
|
echo "3. Configure your nodes through the new web UI"
|
|
echo "4. Reference your old .env file for credentials if needed"
|
|
echo
|
|
echo -e "${YELLOW}Your existing data is preserved at: $INSTALL_DIR${NC}"
|
|
echo
|
|
echo "For more information, see:"
|
|
echo "https://github.com/rcourtman/Pulse/releases/v4.0.0"
|
|
echo -e "${BLUE}=========================================================${NC}"
|
|
echo
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
detect_os() {
|
|
if [[ -f /etc/os-release ]]; then
|
|
. /etc/os-release
|
|
OS=$ID
|
|
VER=$VERSION_ID
|
|
else
|
|
print_error "Cannot detect OS"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
check_existing_installation() {
|
|
if systemctl is-active --quiet $SERVICE_NAME 2>/dev/null; then
|
|
print_info "Pulse is currently running"
|
|
return 0
|
|
elif [[ -f "$INSTALL_DIR/pulse" ]]; then
|
|
print_info "Pulse is installed but not running"
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
install_dependencies() {
|
|
print_info "Installing dependencies..."
|
|
|
|
apt-get update -qq
|
|
apt-get install -y -qq curl wget git build-essential
|
|
|
|
# Install Go if not present or version is too old
|
|
if ! command -v go &> /dev/null; then
|
|
print_info "Installing Go..."
|
|
wget -q -O go.tar.gz https://go.dev/dl/go1.22.5.linux-amd64.tar.gz
|
|
tar -C /usr/local -xzf go.tar.gz
|
|
rm go.tar.gz
|
|
export PATH=$PATH:/usr/local/go/bin
|
|
echo 'export PATH=$PATH:/usr/local/go/bin' >> /etc/profile
|
|
fi
|
|
|
|
# Install Node.js for frontend build
|
|
if ! command -v node &> /dev/null; then
|
|
print_info "Installing Node.js..."
|
|
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
|
|
apt-get install -y nodejs
|
|
fi
|
|
}
|
|
|
|
create_user() {
|
|
if ! id -u pulse &>/dev/null; then
|
|
print_info "Creating pulse user..."
|
|
useradd --system --home-dir $INSTALL_DIR --shell /bin/false pulse
|
|
fi
|
|
}
|
|
|
|
backup_existing() {
|
|
if [[ -d "$CONFIG_DIR" ]]; then
|
|
print_info "Backing up existing configuration..."
|
|
cp -a "$CONFIG_DIR" "${CONFIG_DIR}.backup.$(date +%Y%m%d-%H%M%S)"
|
|
fi
|
|
}
|
|
|
|
download_pulse() {
|
|
print_info "Downloading Pulse..."
|
|
|
|
# Check for forced version first
|
|
if [[ -n "${FORCE_VERSION}" ]]; then
|
|
LATEST_RELEASE="${FORCE_VERSION}"
|
|
print_info "Installing specific version: $LATEST_RELEASE"
|
|
|
|
# Verify the version exists
|
|
if ! curl -fsS "https://api.github.com/repos/$GITHUB_REPO/releases/tags/$LATEST_RELEASE" > /dev/null 2>&1; then
|
|
print_error "Version $LATEST_RELEASE not found"
|
|
exit 1
|
|
fi
|
|
else
|
|
# Check if user has RC channel configured
|
|
UPDATE_CHANNEL="stable"
|
|
|
|
# Allow override via command line
|
|
if [[ -n "${FORCE_CHANNEL}" ]]; then
|
|
UPDATE_CHANNEL="${FORCE_CHANNEL}"
|
|
print_info "Using $UPDATE_CHANNEL channel from command line"
|
|
elif [[ -f "$CONFIG_DIR/system.json" ]]; then
|
|
CONFIGURED_CHANNEL=$(cat "$CONFIG_DIR/system.json" 2>/dev/null | grep -o '"updateChannel"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"\([^"]*\)"$/\1/')
|
|
if [[ "$CONFIGURED_CHANNEL" == "rc" ]]; then
|
|
UPDATE_CHANNEL="rc"
|
|
print_info "RC channel detected in configuration"
|
|
fi
|
|
fi
|
|
|
|
# Get appropriate release based on channel
|
|
if [[ "$UPDATE_CHANNEL" == "rc" ]]; then
|
|
# Get all releases and find the latest (including pre-releases)
|
|
LATEST_RELEASE=$(curl -s https://api.github.com/repos/$GITHUB_REPO/releases | grep '"tag_name":' | head -1 | sed -E 's/.*"([^"]+)".*/\1/')
|
|
else
|
|
# Get latest stable release only
|
|
LATEST_RELEASE=$(curl -s https://api.github.com/repos/$GITHUB_REPO/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
|
|
fi
|
|
|
|
if [[ -z "$LATEST_RELEASE" ]]; then
|
|
print_error "Could not determine latest release"
|
|
exit 1
|
|
fi
|
|
|
|
print_info "Latest version: $LATEST_RELEASE"
|
|
fi
|
|
|
|
# Detect architecture
|
|
ARCH=$(uname -m)
|
|
case $ARCH in
|
|
x86_64)
|
|
PULSE_ARCH="amd64"
|
|
;;
|
|
aarch64)
|
|
PULSE_ARCH="arm64"
|
|
;;
|
|
armv7l)
|
|
PULSE_ARCH="armv7"
|
|
;;
|
|
*)
|
|
print_error "Unsupported architecture: $ARCH"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
print_info "Detected architecture: $ARCH ($PULSE_ARCH)"
|
|
|
|
# Download architecture-specific release
|
|
DOWNLOAD_URL="https://github.com/$GITHUB_REPO/releases/download/$LATEST_RELEASE/pulse-${LATEST_RELEASE}-linux-${PULSE_ARCH}.tar.gz"
|
|
print_info "Downloading from: $DOWNLOAD_URL"
|
|
|
|
# Detect and stop existing service BEFORE downloading (to free the binary)
|
|
EXISTING_SERVICE=$(detect_service_name)
|
|
if systemctl is-active --quiet $EXISTING_SERVICE; then
|
|
print_info "Stopping existing Pulse service ($EXISTING_SERVICE)..."
|
|
systemctl stop $EXISTING_SERVICE
|
|
sleep 2 # Give the process time to fully stop and release the binary
|
|
fi
|
|
|
|
cd /tmp
|
|
if ! wget -q -O pulse.tar.gz "$DOWNLOAD_URL"; then
|
|
print_error "Failed to download Pulse release"
|
|
exit 1
|
|
fi
|
|
|
|
# Extract to temporary directory first
|
|
TEMP_EXTRACT="/tmp/pulse-extract-$$"
|
|
mkdir -p "$TEMP_EXTRACT"
|
|
tar -xzf pulse.tar.gz -C "$TEMP_EXTRACT"
|
|
|
|
# Ensure install directory and bin subdirectory exist
|
|
mkdir -p "$INSTALL_DIR/bin"
|
|
|
|
# Copy Pulse binary to the correct location (/opt/pulse/bin/pulse)
|
|
if [[ -f "$TEMP_EXTRACT/bin/pulse" ]]; then
|
|
cp "$TEMP_EXTRACT/bin/pulse" "$INSTALL_DIR/bin/pulse"
|
|
elif [[ -f "$TEMP_EXTRACT/pulse" ]]; then
|
|
# Fallback for old archives (pre-v4.3.1)
|
|
cp "$TEMP_EXTRACT/pulse" "$INSTALL_DIR/bin/pulse"
|
|
else
|
|
print_error "Pulse binary not found in archive"
|
|
exit 1
|
|
fi
|
|
|
|
chmod +x "$INSTALL_DIR/bin/pulse"
|
|
chown -R pulse:pulse "$INSTALL_DIR"
|
|
|
|
# Create symlink in /usr/local/bin for PATH convenience
|
|
ln -sf "$INSTALL_DIR/bin/pulse" /usr/local/bin/pulse
|
|
print_success "Pulse binary installed to $INSTALL_DIR/bin/pulse"
|
|
print_success "Symlink created at /usr/local/bin/pulse"
|
|
|
|
# Copy VERSION file if present
|
|
if [[ -f "$TEMP_EXTRACT/VERSION" ]]; then
|
|
cp "$TEMP_EXTRACT/VERSION" "$INSTALL_DIR/VERSION"
|
|
chown pulse:pulse "$INSTALL_DIR/VERSION"
|
|
fi
|
|
|
|
# Cleanup
|
|
rm -rf "$TEMP_EXTRACT" pulse.tar.gz
|
|
}
|
|
|
|
setup_directories() {
|
|
print_info "Setting up directories..."
|
|
|
|
# Create directories
|
|
mkdir -p "$CONFIG_DIR"
|
|
mkdir -p "$INSTALL_DIR"
|
|
|
|
# Set permissions
|
|
chown -R pulse:pulse "$CONFIG_DIR" "$INSTALL_DIR"
|
|
chmod 700 "$CONFIG_DIR"
|
|
}
|
|
|
|
install_systemd_service() {
|
|
print_info "Installing systemd service..."
|
|
|
|
# Use existing service name if found, otherwise use default
|
|
EXISTING_SERVICE=$(detect_service_name)
|
|
if [[ "$EXISTING_SERVICE" == "pulse-backend" ]] && [[ -f "/etc/systemd/system/pulse-backend.service" ]]; then
|
|
# Keep using pulse-backend for compatibility (ProxmoxVE)
|
|
SERVICE_NAME="pulse-backend"
|
|
print_info "Using existing service name: pulse-backend"
|
|
fi
|
|
|
|
cat > /etc/systemd/system/$SERVICE_NAME.service << EOF
|
|
[Unit]
|
|
Description=Pulse Monitoring Server
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=pulse
|
|
Group=pulse
|
|
WorkingDirectory=$INSTALL_DIR
|
|
ExecStart=$INSTALL_DIR/bin/pulse
|
|
Restart=always
|
|
RestartSec=3
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
|
Environment="PULSE_DATA_DIR=$CONFIG_DIR"
|
|
|
|
# Security hardening
|
|
NoNewPrivileges=true
|
|
PrivateTmp=true
|
|
ProtectSystem=strict
|
|
ProtectHome=true
|
|
ReadWritePaths=$INSTALL_DIR $CONFIG_DIR
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
systemctl daemon-reload
|
|
}
|
|
|
|
start_pulse() {
|
|
print_info "Starting Pulse..."
|
|
systemctl enable $SERVICE_NAME
|
|
systemctl start $SERVICE_NAME
|
|
|
|
# Wait for service to start
|
|
sleep 3
|
|
|
|
if systemctl is-active --quiet $SERVICE_NAME; then
|
|
print_success "Pulse started successfully"
|
|
else
|
|
print_error "Failed to start Pulse"
|
|
journalctl -u $SERVICE_NAME -n 20
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
print_completion() {
|
|
local IP=$(hostname -I | awk '{print $1}')
|
|
|
|
echo
|
|
print_header
|
|
print_success "Pulse installation completed!"
|
|
echo
|
|
echo -e "${GREEN}Access Pulse at:${NC} http://${IP}:7655"
|
|
echo
|
|
echo -e "${YELLOW}Useful commands:${NC}"
|
|
echo " systemctl status $SERVICE_NAME - Check service status"
|
|
echo " systemctl restart $SERVICE_NAME - Restart Pulse"
|
|
echo " journalctl -u $SERVICE_NAME -f - View logs"
|
|
echo
|
|
}
|
|
|
|
# Main installation flow
|
|
main() {
|
|
print_header
|
|
check_root
|
|
detect_os
|
|
check_pre_v4_installation
|
|
|
|
if check_existing_installation; then
|
|
print_info "Existing Pulse installation detected"
|
|
echo
|
|
echo "What would you like to do?"
|
|
echo "1) Update to latest version"
|
|
echo "2) Reinstall"
|
|
echo "3) Remove"
|
|
echo "4) Cancel"
|
|
read -p "Select option [1-4]: " choice < /dev/tty
|
|
|
|
case $choice in
|
|
1)
|
|
backup_existing
|
|
systemctl stop $SERVICE_NAME || true
|
|
create_user
|
|
download_pulse
|
|
start_pulse
|
|
print_completion
|
|
;;
|
|
2)
|
|
backup_existing
|
|
systemctl stop $SERVICE_NAME || true
|
|
create_user
|
|
download_pulse
|
|
setup_directories
|
|
install_systemd_service
|
|
start_pulse
|
|
print_completion
|
|
;;
|
|
3)
|
|
systemctl stop $SERVICE_NAME || true
|
|
systemctl disable $SERVICE_NAME || true
|
|
rm -f /etc/systemd/system/$SERVICE_NAME.service
|
|
rm -rf "$INSTALL_DIR"
|
|
print_success "Pulse removed successfully"
|
|
;;
|
|
4)
|
|
print_info "Installation cancelled"
|
|
exit 0
|
|
;;
|
|
*)
|
|
print_error "Invalid option"
|
|
exit 1
|
|
;;
|
|
esac
|
|
else
|
|
# Fresh installation
|
|
install_dependencies
|
|
create_user
|
|
setup_directories
|
|
download_pulse
|
|
install_systemd_service
|
|
start_pulse
|
|
print_completion
|
|
fi
|
|
}
|
|
|
|
# Parse command line arguments
|
|
FORCE_VERSION=""
|
|
FORCE_CHANNEL=""
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--rc|--pre|--prerelease)
|
|
FORCE_CHANNEL="rc"
|
|
shift
|
|
;;
|
|
--stable)
|
|
FORCE_CHANNEL="stable"
|
|
shift
|
|
;;
|
|
--version)
|
|
FORCE_VERSION="$2"
|
|
shift 2
|
|
;;
|
|
-h|--help)
|
|
echo "Usage: $0 [OPTIONS]"
|
|
echo "Options:"
|
|
echo " --rc, --pre Install latest RC/pre-release version"
|
|
echo " --stable Install latest stable version (default)"
|
|
echo " --version VERSION Install specific version (e.g., v4.4.0-rc.1)"
|
|
echo " -h, --help Show this help message"
|
|
exit 0
|
|
;;
|
|
*)
|
|
print_error "Unknown option: $1"
|
|
echo "Use --help for usage information"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Export for use in download_pulse function
|
|
export FORCE_VERSION FORCE_CHANNEL
|
|
|
|
# Run main function
|
|
main |