#!/bin/bash set -e # Detect IP address and set environment variables DEFAULT_IP=$(ip route get 1 | awk '{for(i=1;i<=NF;i++) if ($i=="src") {print $(i+1); exit}}') # === Set default environment variables here === export IP_ADDRESS=${IP_ADDRESS:-$DEFAULT_IP} export DU_FQDN=${DU_FQDN:-"pcd.pf9.io"} export REGION_NAME=${REGION_NAME:-"Community"} export SERVICE_CIDR=${SERVICE_CIDR:-"10.21.0.0/16"} export POD_CIDR=${POD_CIDR:-"10.20.0.0/16"} export S3_BUCKET=${S3_BUCKET:-"pcd-community.s3-accelerate.amazonaws.com"} export S3_USER_AGENT=${S3_USER_AGENT:-""} export SKIP_PRECHECKS=${SKIP_PRECHECKS:-"false"} export SKIP_RATING_PROMPT=${SKIP_RATING_PROMPT:-"false"} # To enable kubernetes management plane, set ENABLE_K8S=true before running this script. export ENABLE_K8S=${ENABLE_K8S:-"false"} # Anonymous usage analytics are enabled by default to help improve the product. # To opt out, set TELEMETRY=false before running this script. export TELEMETRY=${TELEMETRY:-"true"} # To skip EULA prompt and auto-accept, set ACCEPT_EULA=true before running this script. export ACCEPT_EULA=${ACCEPT_EULA:-"false"} export STATE_FILE="$HOME/.airctl/state.yaml" export SHOULD_CLEANUP=${SHOULD_CLEANUP:-"false"} # === End of environment variables === if ! ip addr show | grep -q "$IP_ADDRESS"; then echo "Error: IP_ADDRESS '$IP_ADDRESS' is not assigned to any local interface." exit 1 fi # Color codes RED='\033[0;31m' GREEN='\033[0;32m' NC='\033[0m' # Spinner function if [[ $- == *x* ]]; then # For debug mode that shows all output run_with_spinner() { local message="$1" local commands="$2" echo "$message... processing" eval "$commands" local exit_status=$? if [ $exit_status -eq 0 ]; then echo " ${GREEN}Done${NC}" else echo " ${RED}Failed${NC}" exit $exit_status fi } else # For normal operation run_with_spinner() { local message="$1" local commands="$2" echo -n "$message... " # Create temporary files for stdout and stderr local stdout_log local stderr_log stdout_log=$(mktemp) stderr_log=$(mktemp) (eval "$commands") >"$stdout_log" 2>"$stderr_log" & local pid=$! # Simple blinking spinner while kill -0 $pid 2>/dev/null; do echo -ne "${RED}▓${NC}" sleep 0.5 echo -ne "\b${RED}▒${NC}" sleep 0.5 echo -ne "\b" done # Wait for process to finish and check its exit status if wait $pid; then exit_status=0 else exit_status=$? fi if [ $exit_status -eq 0 ]; then echo -e " ${GREEN}Done${NC}" else echo -e " ${RED}Failed${NC}" cat "$stderr_log" rm -f "$stdout_log" "$stderr_log" exit $exit_status fi # Clean up temporary files rm -f "$stdout_log" "$stderr_log" } fi echo "Private Cloud Director Community Edition Deployment Started..." # EULA acceptance prompt if [[ "$ACCEPT_EULA" != "true" ]]; then echo "" echo "By continuing with the installation, you agree to the terms and conditions of the" echo "Private Cloud Director Community Edition EULA." echo "" echo "Please review the EULA at: https://platform9.com/ce-eula" echo "" read -p "Do you accept the terms of the EULA? [Y/N]: " EULA_RESPONSE < /dev/tty if [[ ! "$EULA_RESPONSE" =~ ^[Yy]$ ]]; then echo "Installation aborted. EULA not accepted." exit 1 fi echo "" fi # ----------------------------------------------------------------------------- # Handle reinstall on failure or if the script is run again by mistake. # # This section ensures a clean environment by: # 1. Checking if a previous deployment exists (via the state file). # 2. Detecting if the k3s service is still running. # # If either a previous region is detected or k3s is running, # the user is prompted to clean up the current deployment before reinstalling. # If the user agrees, the script will: # - Run 'airctl unconfigure-du' if the state file indicates cleanup is needed. # - Run 'airctl delete-cluster' if k3s is running. # If the user declines, the script aborts to avoid accidental conflicts. # ----------------------------------------------------------------------------- if [[ -f "$STATE_FILE" ]]; then # Check if regions is an empty array (regions: []) if ! grep -qE '^regions: \[\]' "$STATE_FILE"; then SHOULD_CLEANUP=true fi fi # Check if k3s service is active if systemctl is-active --quiet k3s; then is_k3s_running=true else is_k3s_running=false fi if [[ "$SHOULD_CLEANUP" = true || "$is_k3s_running" = true ]]; then echo -e "\n⚠️ Detected existing or incomplete installation." read -p "Would you like to remove the current deployment and reinstall? [Y/N]: " CLEANUP_CONFIRM < /dev/tty if [[ "$CLEANUP_CONFIRM" =~ ^[Yy]$ ]]; then echo -e "\n➡️ Cleaning up previous installation..." if [[ "$SHOULD_CLEANUP" = true ]]; then run_with_spinner "Running airctl unconfigure-du" " airctl unconfigure-du --force --config /opt/pf9/airctl/conf/airctl-config.yaml < /dev/tty " fi if [[ "$is_k3s_running" = true ]]; then run_with_spinner "Deleting k3s cluster" " airctl delete-cluster --config /opt/pf9/airctl/conf/airctl-config.yaml < /dev/tty " fi rm -rf "$HOME/airctl-logs" rm -rf "$HOME/pcd-ce" else echo "❌ Aborting installation to avoid conflict with existing setup." exit 1 fi fi # Stable version run_with_spinner "Finding latest version" " mkdir -p \${HOME}/pcd-ce && cd \${HOME}/pcd-ce && curl -O https://pcd-community.s3-accelerate.amazonaws.com/stable.txt " # If PCD_VERSION variable is set use that version instead of the stable STABLE_VERSION=$(cat $HOME/pcd-ce/stable.txt) export VERSION=${PCD_VERSION:=$STABLE_VERSION} # Download PCD CE artifacts run_with_spinner "Downloading artifacts" " mkdir -p \${HOME}/pcd-ce && cd \${HOME}/pcd-ce && curl \${S3_USER_AGENT} --silent https://\${S3_BUCKET}/\${VERSION}/index.txt | grep -e airctl -e install-pcd.sh -e pcd-chart.tgz -e options.json -e version.txt -e helm -e kaapi-chart.tgz | awk -v ver=\"\$VERSION\" -v s3b=\"\$S3_BUCKET\" -v s3ua=\"\$S3_USER_AGENT\" '{print \"curl \" s3ua \" -sS \\\"https://\" s3b \"/\" ver \"/\" \$NF \"\\\" -o \${HOME}/pcd-ce/\" \$NF}' | bash " run_with_spinner "Configuring system settings" " # Create sysctl config file echo 'fs.inotify.max_queued_events = 512000 fs.inotify.max_user_instances = 512000 fs.inotify.max_user_watches = 512000 fs.aio-max-nr = 500000 kernel.panic = 10 vm.panic_on_oom = 0' | sudo tee /etc/sysctl.d/99-pf9-airctl.conf > /dev/null && sudo sysctl -p /etc/sysctl.d/99-pf9-airctl.conf " # Install PCD CE artifacts, modify options, and install kubectl run_with_spinner "Installing artifacts and dependencies" " cd ${HOME}/pcd-ce && chmod +x ./install-pcd.sh && ./install-pcd.sh ${VERSION} && sudo cp /opt/pf9/airctl/airctl /usr/bin/ && echo 'export PATH=\$PATH:/opt/pf9/airctl' >> ~/.bashrc && . ~/.bashrc && sed -i 's/\"skip_components\": \"\"/\"skip_components\": \"gnocchi\"/g' /opt/pf9/airctl/conf/options.json && sed -i 's/\"community_edition\": \"false\"/\"community_edition\": \"true\"/g' /opt/pf9/airctl/conf/options.json && sed -i 's/\"enable_kaapi\": \"[^\"]*\"/\"enable_kaapi\": \"'$(echo "${ENABLE_K8S}" | grep -qi '^true$' && echo 'true' || echo 'false')'\"/g' /opt/pf9/airctl/conf/options.json && curl -LO \"https://dl.k8s.io/release/\$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl\" && chmod +x kubectl && sudo mv kubectl /usr/local/bin/ " run_with_spinner "Configuring Docker Mirrors" " sudo mkdir -p /etc/rancher/k3s sudo tee /etc/rancher/k3s/registries.yaml << EOF mirrors: docker.io: endpoint: - "https://mirror.gcr.io" registry-1.docker.io: endpoint: - "https://mirror.gcr.io" EOF " # Start CE airctl ce start --config /opt/pf9/airctl/conf/airctl-config.yaml < /dev/tty echo "" airctl status --config /opt/pf9/airctl/conf/airctl-config.yaml < /dev/tty echo "" # Get credentials and show output BASE=$(echo $DU_FQDN | cut -d. -f1) DOMAIN=$(echo $DU_FQDN | cut -d. -f2-) BASE_LC=$(echo $BASE | tr '[:upper:]' '[:lower:]') REGION_LC=$(echo $REGION_NAME | tr '[:upper:]' '[:lower:]') DOMAIN_LC=$(echo $DOMAIN | tr '[:upper:]' '[:lower:]') echo "Login Details:" echo "URL: https://$BASE_LC-$REGION_LC.$DOMAIN_LC" airctl get-creds --config /opt/pf9/airctl/conf/airctl-config.yaml < /dev/tty echo "" echo "Note: If internal DNS is unavailable, add the management plane FQDN to /etc/hosts on local machine and then log into the UI using the provided credentials."