#!/bin/bash set -euo pipefail shopt -s extglob check_arch() { ARCH=$(uname -m) if [[ "$ARCH" != @("x86_64"|"aarch64") ]]; then echo "Your architecture '$ARCH' is not supported by $SCYLLA_PRODUCT, for supported OS and arch please refer to https://docs.scylladb.com/getting-started/os-support" exit 1 fi } check_os() { OS=$(uname -s) case "$OS" in Linux) ;; *) echo -e "Your operating system $OS is not supported by this installer.\n\nPlease consider using Docker to run Scylla on this machine:\n\nhttps://hub.docker.com/r/scylladb/scylla" && exit 1 ;; esac } echo_msg() { if [ $DRY_RUN -eq 0 ]; then echo $* fi } run_cmd() { CMD=("$@") if [ $DRY_RUN -eq 0 ]; then eval "${CMD[@]}" else echo "${CMD[@]}" fi } check_product() { case "$SCYLLA_PRODUCT" in "scylla") VERSION_CHECK_SYSTEM="scylla" BRANCH_WORD="branch-" ;; "scylla-enterprise") VERSION_CHECK_SYSTEM="enterprise" BRANCH_WORD="enterprise-" ;; *) echo "The product '$SCYLLA_PRODUCT' is not supported by this installer." exit 1 ;; esac } is_supported_version() { if [[ -n "$SCYLLA_VERSION" ]] && [[ $SCYLLA_VERSION != *"nightly"* ]]; then if [[ "$SCYLLA_VERSION" != @(202[3-9].*|5.[4-9]*|[6-9].*) ]]; then supported_versions_message exit 1 fi fi } supported_versions_message() { echo "The specified scylla-version '$SCYLLA_VERSION' has reached End of Life (EOL) or not available. • For OSS supported ScyllaDB versions please refer to https://docs.scylladb.com/stable/getting-started/os-support • For Enterprise supported ScyllaDB versions please refer to https://enterprise.docs.scylladb.com/stable/getting-started/os-support" } is_rc_version() { if [[ $SCYLLA_VERSION == *.rc* ]]; then # replace the 2nd '.' with '~' for RC versions, ex. x.y.rc0 -> x.y~rc0 SCYLLA_VERSION=$(echo $SCYLLA_VERSION | sed 's/\(.*\)\.)*/\1~/') elif [[ $SCYLLA_VERSION == *-rc* ]]; then # replace '-' with '~' for RC versions, ex. x.y.0-rc1 -> x.y.0~rc1 SCYLLA_VERSION=$(echo $SCYLLA_VERSION | sed 's/-/~/') fi } query_default_version() { DEFAULT_SCYLLA_VERSION_RAW=$(curl -s --retry 3 --max-time 5 "https://repositories.scylladb.com/scylla/check_version?system=$VERSION_CHECK_SYSTEM") DEFAULT_SCYLLA_VERSION=$(echo $DEFAULT_SCYLLA_VERSION_RAW | sed -e "s/.*version\":\"\(.*\)\".*/\1/g") } get_latest_patch_for_series() { local series=$1 # Try the version-specific API endpoint first for accurate patch versions local series_info=$(curl -s --retry 3 --max-time 5 "https://repositories.scylladb.com/scylla/check_version?version=$series" 2>/dev/null) if [ $? -eq 0 ] && [ -n "$series_info" ]; then local latest_patch=$(echo "$series_info" | sed -e "s/.*latest_patch_version\":\"\(.*\)\".*/\1/g") # Check if we got a valid patch version for our series if [ -n "$latest_patch" ] && [ "$latest_patch" != "$series_info" ] && [[ "$latest_patch" == "$series"* ]]; then echo "$latest_patch" return fi fi } # Discover all release series by probing the API discover_all_series() { local current_version="$1" local discovered_series="" # Parse current version to get a starting point local current_year=$(echo "$current_version" | cut -d'.' -f1) local current_minor=$(echo "$current_version" | cut -d'.' -f2) if [ $VERBOSE -eq 1 ]; then echo " Debug: Starting discovery from current version $current_version" echo " Debug: Current year: $current_year, current minor: $current_minor" fi # Smart discovery strategy: # 1. Start with current year and probe series around the current version # 2. Check previous year for LTS (.1) releases # 3. Validate each series exists before adding it # Check current year series (from .1 up to current minor + 1 for future releases) local max_minor=$((current_minor + 1)) if [ $max_minor -lt 4 ]; then max_minor=4 # Check at least up to .4 to catch future releases fi for minor in $(seq 1 $max_minor); do series="$current_year.$minor" if [ $VERBOSE -eq 1 ]; then echo " Debug: Testing current year series $series" >&2 fi # Quick test - try to get version info for this series test_response=$(curl -s --retry 3 --max-time 5 "https://repositories.scylladb.com/scylla/check_version?version=$series" 2>/dev/null) # Check if we got a valid response if echo "$test_response" | grep -q '"version"' && ! echo "$test_response" | grep -q '"error"'; then extracted_version=$(echo "$test_response" | sed -e "s/.*version\":\"\([^\"]*\)\".*/\1/g") # Validate the extracted version looks like a proper version if echo "$extracted_version" | grep -q "^$series\.[0-9]\+$"; then discovered_series="$discovered_series $series" if [ $VERBOSE -eq 1 ]; then echo " Debug: ✓ Found series $series (version: $extracted_version)" >&2 fi fi else if [ $VERBOSE -eq 1 ]; then echo " Debug: ✗ Series $series not found or invalid" >&2 fi fi done # Check previous year LTS (.1 release) - ScyllaDB maintains 2 LTS at a time prev_year=$((current_year - 1)) if [ $prev_year -ge 2024 ]; then # ScyllaDB started new versioning in 2024 series="$prev_year.1" if [ $VERBOSE -eq 1 ]; then echo " Debug: Testing previous year LTS $series" >&2 fi test_response=$(curl -s --retry 3 --max-time 5 "https://repositories.scylladb.com/scylla/check_version?version=$series" 2>/dev/null) if echo "$test_response" | grep -q '"version"' && ! echo "$test_response" | grep -q '"error"'; then extracted_version=$(echo "$test_response" | sed -e "s/.*version\":\"\([^\"]*\)\".*/\1/g") if echo "$extracted_version" | grep -q "^$series\.[0-9]\+$"; then discovered_series="$discovered_series $series" if [ $VERBOSE -eq 1 ]; then echo " Debug: ✓ Found previous year LTS $series (version: $extracted_version)" >&2 fi fi fi fi echo "$discovered_series" } # Check if a series is actively supported (not EOL) is_series_supported() { local series="$1" local current_version="$2" # Parse versions local series_year=$(echo "$series" | cut -d'.' -f1) local series_minor=$(echo "$series" | cut -d'.' -f2) local current_year=$(echo "$current_version" | cut -d'.' -f1) local current_minor=$(echo "$current_version" | cut -d'.' -f2) # LTS series (.1 releases) - ScyllaDB supports 2 LTS releases at a time if [ "$series_minor" = "1" ]; then # Current year LTS is always supported if [ "$series_year" = "$current_year" ]; then return 0 fi # Previous year LTS is supported if current year has an LTS if [ "$series_year" = "$((current_year - 1))" ] && [ "$current_minor" -ge 1 ]; then return 0 fi # No longer supported if it's 2+ years old return 1 fi # Non-LTS series - only current year series are supported if [ "$series_year" = "$current_year" ]; then return 0 fi # Non-LTS from previous years are EOL return 1 } list_active_releases() { echo "ScyllaDB Active Releases (Latest to Oldest):" echo "=============================================" # Check if curl is available if ! command -v curl &> /dev/null; then echo "Error: curl is not installed or not available in PATH" echo "Please install curl to use this feature" exit 1 fi system="scylla" # Get current version to understand what's latest if [ $VERBOSE -eq 1 ]; then echo " Debug: Querying https://repositories.scylladb.com/scylla/check_version?system=$system" fi current_version_raw=$(curl -s --retry 3 --max-time 5 "https://repositories.scylladb.com/scylla/check_version?system=$system" 2>/dev/null) curl_exit_code=$? if [ $VERBOSE -eq 1 ]; then echo " Debug: curl exit code: $curl_exit_code" echo " Debug: raw response: $current_version_raw" fi current_version=$(echo "$current_version_raw" | sed -e "s/.*version\":\"\(.*\)\".*/\1/g") if [ $VERBOSE -eq 1 ]; then echo " Debug: extracted version: $current_version" fi if [ $curl_exit_code -ne 0 ]; then echo " Network error: Failed to connect to ScyllaDB repository (curl exit code: $curl_exit_code)" echo " Please check your internet connection and try again" return elif [ -z "$current_version" ] || [ "$current_version" = "" ] || [ "$current_version" = "$current_version_raw" ]; then echo " Unable to parse version information" echo " Raw response: $current_version_raw" echo " Please try again later or check if the API format has changed" return fi # Dynamically discover all available series if [ $VERBOSE -eq 1 ]; then echo " Debug: Starting series discovery..." >&2 fi all_series=$(discover_all_series "$current_version") if [ $VERBOSE -eq 1 ]; then echo " Debug: Discovered series: $all_series" >&2 fi # Filter to only supported/active series active_series="" for series in $all_series; do if is_series_supported "$series" "$current_version"; then active_series="$active_series $series" if [ $VERBOSE -eq 1 ]; then echo " Debug: $series is supported" >&2 fi else if [ $VERBOSE -eq 1 ]; then echo " Debug: $series is EOL" >&2 fi fi done # Sort active series in descending order (latest to oldest) active_series=$(echo "$active_series" | tr ' ' '\n' | sort -V -r | uniq | tr '\n' ' ') if [ -z "$active_series" ]; then echo " No active releases found. This might indicate an API issue." return fi # Parse current version info for latest marker current_year=$(echo "$current_version" | cut -d'.' -f1) current_minor=$(echo "$current_version" | cut -d'.' -f2) current_series="$current_year.$current_minor" # Display each active series with latest patch version for series in $active_series; do series=$(echo "$series" | xargs) # trim whitespace if [ -n "$series" ]; then latest_patch=$(get_latest_patch_for_series "$series") # Determine if this is LTS series_minor=$(echo "$series" | cut -d'.' -f2) if [ "$series_minor" = "1" ]; then lts_marker=" [LTS]" else lts_marker="" fi # Check if this is the current latest series if [ "$series" = "$current_series" ]; then support_status=" - latest" else support_status="" fi echo " $series: $latest_patch$lts_marker$support_status" fi done } packages_update() { if [[ "$NAME" == @(Ubuntu|Debian)* ]]; then run_cmd apt update run_cmd apt-get install $APT_FLAGS curl gnupg2 elif [[ "$NAME" == @(Amazon|Red Hat)* ]]; then run_cmd yum install $YUM_QUIET_CMD_PARAM hostname iputils fi } setup_install() { SCYLLA_GPG_KEY="d0a112e067426ab2 491c93b9de7496a7 a43e06657bac99e3" if [[ -n "$SCYLLA_REPO_FILE_URL" ]]; then SCYLLA_URL=${SCYLLA_REPO_FILE_URL} SCYLLA_PRODUCT_VERSION="${SCYLLA_PRODUCT}" else if [[ $SCYLLA_VERSION == *"nightly"* ]]; then SCYLLA_RELEASE=$(echo $SCYLLA_VERSION | cut -d'-' -f 2) if [ $SCYLLA_RELEASE == "master" ] || [ $SCYLLA_RELEASE == "enterprise" ]; then BRANCH_WORD="" fi if [ $1 == "debian" ]; then SCYLLA_URL="https://downloads.scylladb.com/unstable/$SCYLLA_PRODUCT/$BRANCH_WORD$SCYLLA_RELEASE/deb/unified/latest/scylladb-$SCYLLA_RELEASE/scylla.list" SCYLLA_PRODUCT_VERSION=$SCYLLA_PRODUCT elif [ $1 == "ubuntu" ]; then SCYLLA_URL="https://downloads.scylladb.com/unstable/$SCYLLA_PRODUCT/$BRANCH_WORD$SCYLLA_RELEASE/deb/unified/latest/scylladb-$SCYLLA_RELEASE/scylla.list" SCYLLA_PRODUCT_VERSION=$SCYLLA_PRODUCT else set_rpm_install_tool SCYLLA_URL="https://downloads.scylladb.com/unstable/$SCYLLA_PRODUCT/$BRANCH_WORD$SCYLLA_RELEASE/rpm/centos/latest/scylla.repo" SCYLLA_PRODUCT_VERSION=$SCYLLA_PRODUCT fi else SCYLLA_RELEASE=$(echo $SCYLLA_VERSION | sed -e "s/\([[:digit:]]\+.[[:digit:]]\+\).*/\1/g") if [ $1 == "debian" ]; then is_rc_version SCYLLA_URL="https://downloads.scylladb.com/deb/debian/scylla-$SCYLLA_RELEASE.list" SCYLLA_PRODUCT_VERSION="${SCYLLA_PRODUCT}=$SCYLLA_VERSION*" elif [ $1 == "ubuntu" ]; then is_rc_version SCYLLA_URL="https://downloads.scylladb.com/deb/ubuntu/scylla-$SCYLLA_RELEASE.list" SCYLLA_PRODUCT_VERSION="${SCYLLA_PRODUCT}=$SCYLLA_VERSION*" else set_rpm_install_tool SCYLLA_URL="https://downloads.scylladb.com/rpm/centos/scylla-$SCYLLA_RELEASE.repo" SCYLLA_PRODUCT_VERSION="$SCYLLA_PRODUCT-$SCYLLA_VERSION" fi fi fi } get_full_version() { PATCH_VERSION=$(echo $SCYLLA_VERSION | awk -v FS='.' '{print $3}') if [ -n "$PATCH_VERSION" ] && [ -z "$DEFAULT_SCYLLA_VERSION" ] || [[ $SCYLLA_VERSION == *rc* ]]; then FULL_SCYLLA_VERSION=$(apt-cache madison ${SCYLLA_PRODUCT} | grep -F -w $SCYLLA_VERSION | cut -d'|' -f 2 | sed 's/[[:space:]]//g' | head -n1) PACKAGES_LIST=',-server,-cqlsh,-kernel-conf,-node-exporter,-conf,-python3' [[ "$SCYLLA_VERSION" == 2025.1* ]] && PACKAGES_LIST+=",-tools,-tools-core" [[ "$SCYLLA_VERSION" == 2024.1* ]] && PACKAGES_LIST+=",-tools,-tools-core,-jmx" SCYLLA_PRODUCT_VERSION="${SCYLLA_PRODUCT}{$PACKAGES_LIST}=$FULL_SCYLLA_VERSION" fi } main() { DRY_RUN=0 SCYLLA_REPO_FILE_URL="" DEBUG_RUN=0 VERBOSE=0 USAGE=0 DEFAULT_SCYLLA_PRODUCT="scylla" SCYLLA_PRODUCT="$DEFAULT_SCYLLA_PRODUCT" SCYLLA_VERSION="" DEFAULT_SCYLLA_VERSION="" while [ $# -gt 0 ]; do case "$1" in "-h" | "--help") USAGE=1 shift 1 ;; "--scylla-product") SCYLLA_PRODUCT="$2" shift 2 ;; "--scylla-version") SCYLLA_VERSION="$2" shift 2 ;; "--list-active-releases") list_active_releases exit 0 ;; "--dry-run") DRY_RUN=1 shift 1 ;; "--debug-run") DEBUG_RUN=1 shift 1 ;; "--scylla-repo-file-url") SCYLLA_REPO_FILE_URL="$2" shift 2 ;; "--verbose") VERBOSE=1 shift 1 ;; *) USAGE=1 shift 1 ;; esac done if [ $USAGE -eq 1 ]; then usage exit 1 fi RETRY_OPTION="-o Acquire::Retries=3" if [ $VERBOSE -eq 1 ]; then YUM_QUIET_CMD_PARAM="--assumeyes" APT_FLAGS="--assume-yes $RETRY_OPTION" else YUM_QUIET_CMD_PARAM="--assumeyes --quiet" APT_FLAGS="-qq $RETRY_OPTION" fi check_product check_os is_supported_version # os-release may be missing in container environment by default. if [ -f "/etc/os-release" ]; then . /etc/os-release elif [ -f "/etc/arch-release" ]; then export ID=arch else echo "/etc/os-release missing." exit 1 fi packages_update if [ -z "$SCYLLA_VERSION" ] && [ -z "$SCYLLA_REPO_FILE_URL" ]; then query_default_version SCYLLA_VERSION="$DEFAULT_SCYLLA_VERSION" fi check_arch setup_install $ID case "$ID" in "amzn") amzn_install ;; "centos"|"rocky") centos_install ;; "debian"|"ubuntu") deb_install ;; "fedora") fedora_install ;; "almalinux") centos_install ;; "ol") ol_install ;; "rhel") rhel_install ;; *) echo "Operating system '$ID' is not supported by this installer." && exit 1 esac echo_msg "Scylla installation done!" } usage() { cat 1>&2 <] - Scylla version to install (default: $DEFAULT_SCYLLA_VERSION) Version examples: --scylla-version nightly-master will install master latest nightly version --scylla-version nightly-5.4 will install 5.4 latest nightly version For enterprise product: --scylla-version nightly-enterprise will install enterprise latest nightly version --scylla-version nightly-202x.1 will install 202x.1 latest nightly version DEBUG OPTIONS: [--scylla-repo-file-url ] URL to test installation from a custom URL. DEBUG FLAGS: [--debug-run] - use slim/minimal dockers when needed. [--dry-run] - Prints out commands instead of executing them. [--verbose] - Runs install commands with no quiet option, and more info. EOF } require_cmd() { CMD=$1 if ! command -v "$CMD" &> /dev/null then echo "Please make sure '$CMD' is installed on this machine." exit fi } set_rpm_install_tool() { RPM_INSTALL_TOOL="yum" if [ $DEBUG_RUN -eq 1 ]; then RPM_INSTALL_TOOL="microdnf" fi require_cmd $RPM_INSTALL_TOOL } install_rpm() { ret=0 run_cmd curl -f -s -L -o /etc/yum.repos.d/scylla.repo "$SCYLLA_URL" || ret=$? if [ $ret -ne 0 ]; then supported_versions_message exit 1 fi run_cmd $RPM_INSTALL_TOOL install $YUM_QUIET_CMD_PARAM $SCYLLA_PRODUCT_VERSION } check_amzn_version() { case "$VERSION_ID" in 2*) return 1 ;; *) return 0 ;; esac } amzn_install() { if check_amzn_version = 0; then echo "Amazon Linux $VERSION_ID is not supported by this installer." exit 1 fi echo_msg "Installing Scylla version $SCYLLA_VERSION for Amazon Linux ..." set_rpm_install_tool install_rpm } check_centos_version() { case "$VERSION_ID" in 8*|9*|10*) return 1 ;; *) return 0 ;; esac } centos_install() { if check_centos_version = 0; then echo "CentOS $VERSION_ID is not supported by this installer." exit 1 fi set_rpm_install_tool echo_msg "Installing Scylla version $SCYLLA_VERSION for CentOS ..." install_rpm } fedora_install() { echo_msg "Installing Scylla version $SCYLLA_VERSION for Fedora ..." set_rpm_install_tool install_rpm } check_rhel_version() { case "$VERSION_ID" in 7.?|8*|9*|10*) return 1 ;; *) return 0 ;; esac } rhel_install() { if check_rhel_version = 0; then echo "Red Hat Enterprise Linux $VERSION_ID is not supported by this installer." exit 1 fi echo_msg "Installing Scylla version $SCYLLA_VERSION for Red Hat Enterprise Linux ..." set_rpm_install_tool install_rpm } check_ol_version() { case "$VERSION_ID" in 7.?|8*) return 1 ;; *) return 0 ;; esac } ol_install() { if check_ol_version = 0; then echo "Oracle Linux $VERSION_ID is not supported by this installer." exit 1 fi echo_msg "Installing Scylla version $SCYLLA_VERSION for Oracle Linux ..." set_rpm_install_tool install_rpm } check_debian_ubuntu_version() { if [[ "$ID" == "debian" ]]; then case "$VERSION_ID" in "9"|"10"|"11"|"12") return 0 ;; *) echo "$ID $VERSION_ID is not supported by this installer." exit 1 ;; esac elif [[ "$ID" == "ubuntu" ]]; then case "$VERSION_ID" in "20.04"|"22.04"|"24.04") return 0 ;; *) echo "$ID $VERSION_ID is not supported by this installer." exit 1 ;; esac fi } deb_install() { if check_debian_ubuntu_version; then export DEBIAN_FRONTEND=noninteractive APT_KEYS_DIR='/etc/apt/keyrings' ret=0 run_cmd curl -f -s -L -o /etc/apt/sources.list.d/scylla.list "$SCYLLA_URL" || ret=$? if [ $ret -ne 0 ]; then supported_versions_message exit 1 fi echo_msg "Installing Scylla version $SCYLLA_VERSION for $NAME ..." run_cmd apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys $SCYLLA_GPG_KEY run_cmd mkdir -p $APT_KEYS_DIR && gpg --homedir /tmp --no-default-keyring --keyring $APT_KEYS_DIR/scylladb.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys $SCYLLA_GPG_KEY run_cmd apt update $APT_FLAGS get_full_version run_cmd apt-get install $APT_FLAGS $SCYLLA_PRODUCT_VERSION fi } main "$@"