1152 lines
34 KiB
Bash
Executable File
1152 lines
34 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# AtlasOS Installation Script for Ubuntu 24.04
|
|
# Installs AtlasOS storage controller with infrastructure gap consideration
|
|
#
|
|
# Usage: sudo ./install.sh [options]
|
|
#
|
|
# Note: Run this script from the atlas repository root directory
|
|
#
|
|
|
|
set -e
|
|
set -o pipefail
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Default values
|
|
INSTALL_DIR="/opt/atlas"
|
|
DATA_DIR="/var/lib/atlas"
|
|
CONFIG_DIR="/etc/atlas"
|
|
SERVICE_USER="atlas"
|
|
LOG_DIR="/var/log/atlas"
|
|
BACKUP_DIR="/var/lib/atlas/backups"
|
|
HTTP_ADDR=":8080"
|
|
DB_PATH="/var/lib/atlas/atlas.db"
|
|
BUILD_BINARIES=true
|
|
SKIP_DEPS=false
|
|
REPO_DIR=""
|
|
FIREWALL_ACTIVE=false
|
|
|
|
# Parse command line arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--install-dir)
|
|
INSTALL_DIR="$2"
|
|
shift 2
|
|
;;
|
|
--data-dir)
|
|
DATA_DIR="$2"
|
|
shift 2
|
|
;;
|
|
--skip-deps)
|
|
SKIP_DEPS=true
|
|
shift
|
|
;;
|
|
--skip-build)
|
|
BUILD_BINARIES=false
|
|
shift
|
|
;;
|
|
--http-addr)
|
|
HTTP_ADDR="$2"
|
|
shift 2
|
|
;;
|
|
--repo-dir)
|
|
REPO_DIR="$2"
|
|
shift 2
|
|
;;
|
|
-h|--help)
|
|
echo "AtlasOS Installation Script"
|
|
echo ""
|
|
echo "Usage: sudo ./install.sh [options]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --install-dir DIR Installation directory (default: /opt/atlas)"
|
|
echo " --data-dir DIR Data directory (default: /var/lib/atlas)"
|
|
echo " --skip-deps Skip dependency installation"
|
|
echo " --skip-build Skip building binaries (use existing)"
|
|
echo " --http-addr ADDR HTTP address (default: :8080)"
|
|
echo " --repo-dir DIR Repository directory (if not in current dir)"
|
|
echo " -h, --help Show this help message"
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Check if running as root
|
|
if [[ $EUID -ne 0 ]]; then
|
|
echo -e "${RED}Error: This script must be run as root (use sudo)${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# Get script directory early (for path resolution)
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
# Detect distribution and validate Ubuntu 24.04
|
|
detect_distro() {
|
|
if [[ -f /etc/os-release ]]; then
|
|
. /etc/os-release
|
|
DISTRO=$ID
|
|
VERSION=$VERSION_ID
|
|
CODENAME=$VERSION_CODENAME
|
|
else
|
|
echo -e "${RED}Error: Cannot detect Linux distribution${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# Validate Ubuntu 24.04
|
|
if [[ "$DISTRO" != "ubuntu" ]]; then
|
|
echo -e "${RED}Error: This installer is specific to Ubuntu${NC}"
|
|
echo " Detected: $DISTRO $VERSION"
|
|
echo " Please use the generic installer or install manually"
|
|
exit 1
|
|
fi
|
|
|
|
# Check for Ubuntu 24.04 (Noble Numbat)
|
|
if [[ "$VERSION" != "24.04" ]] && [[ "$CODENAME" != "noble" ]]; then
|
|
echo -e "${YELLOW}Warning: This installer is optimized for Ubuntu 24.04${NC}"
|
|
echo " Detected: Ubuntu $VERSION ($CODENAME)"
|
|
echo " Continuing anyway, but some features may not work correctly"
|
|
read -p "Continue anyway? (y/n) " -n 1 -r
|
|
echo ""
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
exit 1
|
|
fi
|
|
else
|
|
echo -e "${GREEN}✓ Detected Ubuntu 24.04 (Noble Numbat)${NC}"
|
|
fi
|
|
}
|
|
|
|
# Pre-flight checks for infrastructure gaps
|
|
preflight_checks() {
|
|
echo -e "${GREEN}Running pre-flight checks...${NC}"
|
|
|
|
local errors=0
|
|
local warnings=0
|
|
|
|
# Check network connectivity
|
|
echo -n " Checking network connectivity... "
|
|
if ping -c 1 -W 2 8.8.8.8 &>/dev/null || ping -c 1 -W 2 1.1.1.1 &>/dev/null; then
|
|
echo -e "${GREEN}✓${NC}"
|
|
else
|
|
echo -e "${YELLOW}⚠ No internet connectivity${NC}"
|
|
warnings=$((warnings + 1))
|
|
fi
|
|
|
|
# Check if apt is working
|
|
echo -n " Checking package manager... "
|
|
if apt-get update &>/dev/null; then
|
|
echo -e "${GREEN}✓${NC}"
|
|
else
|
|
echo -e "${RED}✗ APT not working${NC}"
|
|
errors=$((errors + 1))
|
|
fi
|
|
|
|
# Check disk space (need at least 2GB free)
|
|
echo -n " Checking disk space... "
|
|
AVAILABLE_SPACE=$(df / | tail -1 | awk '{print $4}')
|
|
if [[ $AVAILABLE_SPACE -gt 2097152 ]]; then # 2GB in KB
|
|
echo -e "${GREEN}✓ ($(numfmt --to=iec-i --suffix=B $((AVAILABLE_SPACE * 1024))) available)${NC}"
|
|
else
|
|
echo -e "${YELLOW}⚠ Low disk space ($(numfmt --to=iec-i --suffix=B $((AVAILABLE_SPACE * 1024))) available)${NC}"
|
|
warnings=$((warnings + 1))
|
|
fi
|
|
|
|
# Check if systemd is available
|
|
echo -n " Checking systemd... "
|
|
if systemctl --version &>/dev/null; then
|
|
echo -e "${GREEN}✓${NC}"
|
|
else
|
|
echo -e "${RED}✗ systemd not available${NC}"
|
|
errors=$((errors + 1))
|
|
fi
|
|
|
|
# Check kernel version (Ubuntu 24.04 should have 6.8+)
|
|
echo -n " Checking kernel version... "
|
|
KERNEL_VERSION=$(uname -r | cut -d. -f1,2)
|
|
KERNEL_MAJOR=$(echo $KERNEL_VERSION | cut -d. -f1)
|
|
KERNEL_MINOR=$(echo $KERNEL_VERSION | cut -d. -f2)
|
|
if [[ $KERNEL_MAJOR -gt 6 ]] || [[ $KERNEL_MAJOR -eq 6 && $KERNEL_MINOR -ge 8 ]]; then
|
|
echo -e "${GREEN}✓ ($(uname -r))${NC}"
|
|
else
|
|
echo -e "${YELLOW}⚠ Kernel $(uname -r) may not fully support all features${NC}"
|
|
warnings=$((warnings + 1))
|
|
fi
|
|
|
|
# Check if running in container (may need special handling)
|
|
echo -n " Checking environment... "
|
|
if [[ -f /.dockerenv ]] || grep -qa container=lxc /proc/1/environ 2>/dev/null; then
|
|
echo -e "${YELLOW}⚠ Running in container (some features may be limited)${NC}"
|
|
warnings=$((warnings + 1))
|
|
else
|
|
echo -e "${GREEN}✓${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
if [[ $errors -gt 0 ]]; then
|
|
echo -e "${RED}Pre-flight checks failed with $errors error(s)${NC}"
|
|
exit 1
|
|
elif [[ $warnings -gt 0 ]]; then
|
|
echo -e "${YELLOW}Pre-flight checks completed with $warnings warning(s)${NC}"
|
|
else
|
|
echo -e "${GREEN}All pre-flight checks passed${NC}"
|
|
fi
|
|
echo ""
|
|
}
|
|
|
|
# Fix common infrastructure gaps
|
|
fix_infrastructure_gaps() {
|
|
echo -e "${GREEN}Fixing infrastructure gaps...${NC}"
|
|
|
|
# Ensure universe repository is enabled (required for some packages on Ubuntu)
|
|
echo -n " Checking universe repository... "
|
|
if ! grep -q "^deb.*universe" /etc/apt/sources.list /etc/apt/sources.list.d/*.list 2>/dev/null; then
|
|
echo -e "${YELLOW}⚠ Enabling universe repository...${NC}"
|
|
add-apt-repository -y universe 2>/dev/null || {
|
|
echo "deb http://archive.ubuntu.com/ubuntu $(lsb_release -cs) universe" >> /etc/apt/sources.list
|
|
apt-get update
|
|
}
|
|
echo -e "${GREEN}✓ Universe repository enabled${NC}"
|
|
else
|
|
echo -e "${GREEN}✓${NC}"
|
|
fi
|
|
|
|
# Ensure multiverse repository is enabled (for some packages)
|
|
echo -n " Checking multiverse repository... "
|
|
if ! grep -q "^deb.*multiverse" /etc/apt/sources.list /etc/apt/sources.list.d/*.list 2>/dev/null; then
|
|
echo -e "${YELLOW}⚠ Enabling multiverse repository...${NC}"
|
|
add-apt-repository -y multiverse 2>/dev/null || {
|
|
echo "deb http://archive.ubuntu.com/ubuntu $(lsb_release -cs) multiverse" >> /etc/apt/sources.list
|
|
apt-get update
|
|
}
|
|
echo -e "${GREEN}✓ Multiverse repository enabled${NC}"
|
|
else
|
|
echo -e "${GREEN}✓${NC}"
|
|
fi
|
|
|
|
# Load ZFS kernel module if not loaded
|
|
echo -n " Checking ZFS kernel module... "
|
|
if ! lsmod | grep -q "^zfs"; then
|
|
echo -e "${YELLOW}⚠ Loading ZFS kernel module...${NC}"
|
|
modprobe zfs 2>/dev/null || {
|
|
echo -e "${YELLOW} ZFS module not available (will be installed with zfsutils-linux)${NC}"
|
|
}
|
|
fi
|
|
echo -e "${GREEN}✓${NC}"
|
|
|
|
# Ensure ZFS module loads on boot
|
|
if ! grep -q "^zfs" /etc/modules-load.d/*.conf 2>/dev/null && ! grep -q "^zfs" /etc/modules 2>/dev/null; then
|
|
echo "zfs" > /etc/modules-load.d/zfs.conf
|
|
echo -e "${GREEN} ✓ ZFS module will load on boot${NC}"
|
|
fi
|
|
|
|
# Check and configure firewall (UFW)
|
|
echo -n " Checking firewall... "
|
|
if command -v ufw &>/dev/null; then
|
|
if ufw status | grep -q "Status: active"; then
|
|
echo -e "${YELLOW}⚠ UFW is active${NC}"
|
|
echo " Will configure firewall rules after installation"
|
|
FIREWALL_ACTIVE=true
|
|
else
|
|
echo -e "${GREEN}✓ (inactive)${NC}"
|
|
FIREWALL_ACTIVE=false
|
|
fi
|
|
else
|
|
echo -e "${GREEN}✓ (not installed)${NC}"
|
|
FIREWALL_ACTIVE=false
|
|
fi
|
|
|
|
echo -e "${GREEN}Infrastructure gaps fixed${NC}"
|
|
echo ""
|
|
}
|
|
|
|
# Install dependencies for Ubuntu 24.04
|
|
install_dependencies() {
|
|
echo -e "${GREEN}Installing dependencies for Ubuntu 24.04...${NC}"
|
|
|
|
# Update package lists
|
|
echo " Updating package lists..."
|
|
apt-get update -qq
|
|
|
|
# Install essential build tools first
|
|
echo " Installing build essentials..."
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
|
|
build-essential \
|
|
git \
|
|
curl \
|
|
wget \
|
|
ca-certificates \
|
|
software-properties-common \
|
|
apt-transport-https
|
|
|
|
# Install ZFS utilities (Ubuntu 24.04 specific)
|
|
echo " Installing ZFS utilities..."
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
|
|
zfsutils-linux \
|
|
zfs-zed \
|
|
zfs-initramfs || {
|
|
echo -e "${YELLOW}Warning: ZFS installation may require additional setup${NC}"
|
|
}
|
|
|
|
# Install storage services
|
|
echo " Installing storage services..."
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
|
|
samba \
|
|
samba-common-bin \
|
|
nfs-kernel-server \
|
|
rpcbind
|
|
|
|
# Install iSCSI target (Ubuntu 24.04 uses targetcli-fb)
|
|
echo " Installing iSCSI target..."
|
|
if apt-cache show targetcli-fb &>/dev/null; then
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq targetcli-fb
|
|
# Create symlink for compatibility
|
|
if ! command -v targetcli &>/dev/null && command -v targetcli-fb &>/dev/null; then
|
|
ln -sf $(which targetcli-fb) /usr/local/bin/targetcli
|
|
fi
|
|
else
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq targetcli || {
|
|
echo -e "${YELLOW}Warning: targetcli not available, iSCSI features may be limited${NC}"
|
|
}
|
|
fi
|
|
|
|
# Install database
|
|
echo " Installing SQLite..."
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq sqlite3 libsqlite3-dev
|
|
|
|
# Install Go compiler (Ubuntu 24.04 has Go 1.22+)
|
|
echo " Installing Go compiler..."
|
|
if ! command -v go &>/dev/null; then
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq golang-go || {
|
|
echo -e "${YELLOW}Warning: Go not in repositories, installing from official source...${NC}"
|
|
# Fallback: install Go from official source
|
|
GO_VERSION="1.22.0"
|
|
wget -q https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz -O /tmp/go.tar.gz
|
|
rm -rf /usr/local/go
|
|
tar -C /usr/local -xzf /tmp/go.tar.gz
|
|
ln -sf /usr/local/go/bin/go /usr/local/bin/go
|
|
ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt
|
|
rm /tmp/go.tar.gz
|
|
}
|
|
fi
|
|
|
|
# Verify Go installation
|
|
if command -v go &>/dev/null; then
|
|
GO_VERSION=$(go version | awk '{print $3}')
|
|
echo -e "${GREEN} ✓ Go $GO_VERSION installed${NC}"
|
|
else
|
|
echo -e "${RED}Error: Go installation failed${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# Install additional utilities
|
|
echo " Installing additional utilities..."
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
|
|
openssl \
|
|
net-tools \
|
|
iproute2 \
|
|
systemd \
|
|
journalctl
|
|
|
|
echo -e "${GREEN}Dependencies installed successfully${NC}"
|
|
echo ""
|
|
}
|
|
|
|
# Create system user
|
|
create_user() {
|
|
echo -e "${GREEN}Creating system user...${NC}"
|
|
|
|
if ! id "$SERVICE_USER" &>/dev/null; then
|
|
useradd -r -s /bin/false -d "$DATA_DIR" "$SERVICE_USER"
|
|
echo -e "${GREEN}User $SERVICE_USER created${NC}"
|
|
else
|
|
echo -e "${YELLOW}User $SERVICE_USER already exists${NC}"
|
|
fi
|
|
|
|
# Add user to disk group for block device access (required for ZFS)
|
|
if getent group disk > /dev/null 2>&1; then
|
|
usermod -a -G disk "$SERVICE_USER"
|
|
echo -e "${GREEN}Added $SERVICE_USER to disk group${NC}"
|
|
fi
|
|
|
|
# Create sudoers configuration for ZFS commands
|
|
echo -e "${GREEN}Configuring sudo for ZFS operations...${NC}"
|
|
cat > /etc/sudoers.d/atlas-zfs <<EOF
|
|
# Allow atlas user to run ZFS commands without password
|
|
# This is required for ZFS pool operations
|
|
$SERVICE_USER ALL=(ALL) NOPASSWD: /usr/sbin/zpool, /usr/bin/zpool, /sbin/zpool, /usr/sbin/zfs, /usr/bin/zfs, /sbin/zfs
|
|
EOF
|
|
chmod 440 /etc/sudoers.d/atlas-zfs
|
|
echo -e "${GREEN}Sudo configuration created${NC}"
|
|
}
|
|
|
|
# Create directories
|
|
create_directories() {
|
|
echo -e "${GREEN}Creating directories...${NC}"
|
|
|
|
mkdir -p "$INSTALL_DIR/bin"
|
|
mkdir -p "$DATA_DIR"
|
|
mkdir -p "$CONFIG_DIR"
|
|
mkdir -p "$LOG_DIR"
|
|
mkdir -p "$BACKUP_DIR"
|
|
mkdir -p "$CONFIG_DIR/tls"
|
|
|
|
# Set ownership
|
|
chown -R "$SERVICE_USER:$SERVICE_USER" "$DATA_DIR"
|
|
chown -R "$SERVICE_USER:$SERVICE_USER" "$LOG_DIR"
|
|
chown -R "$SERVICE_USER:$SERVICE_USER" "$BACKUP_DIR"
|
|
chown -R "$SERVICE_USER:$SERVICE_USER" "$CONFIG_DIR"
|
|
|
|
# Set permissions
|
|
chmod 755 "$INSTALL_DIR"
|
|
chmod 755 "$INSTALL_DIR/bin"
|
|
chmod 700 "$DATA_DIR"
|
|
chmod 700 "$CONFIG_DIR"
|
|
chmod 750 "$LOG_DIR"
|
|
chmod 750 "$BACKUP_DIR"
|
|
|
|
echo -e "${GREEN}Directories created${NC}"
|
|
}
|
|
|
|
# Build binaries
|
|
build_binaries() {
|
|
if [[ "$BUILD_BINARIES" == "false" ]]; then
|
|
echo -e "${YELLOW}Skipping binary build${NC}"
|
|
return
|
|
fi
|
|
|
|
echo -e "${GREEN}Building binaries...${NC}"
|
|
|
|
# Check if we're in the right directory
|
|
# Look for go.mod and internal/ directory (indicates we're in the repo root)
|
|
BUILD_DIR=""
|
|
|
|
# Function to check if directory is valid repo root
|
|
is_repo_root() {
|
|
local dir="$1"
|
|
[[ -f "$dir/go.mod" ]] && [[ -d "$dir/internal" ]]
|
|
}
|
|
|
|
# Try REPO_DIR first
|
|
if [[ -n "$REPO_DIR" ]] && is_repo_root "$REPO_DIR"; then
|
|
BUILD_DIR="$(cd "$REPO_DIR" && pwd)"
|
|
# Try current directory
|
|
elif is_repo_root "."; then
|
|
BUILD_DIR="$(pwd)"
|
|
# Try script directory
|
|
elif is_repo_root "$SCRIPT_DIR"; then
|
|
BUILD_DIR="$SCRIPT_DIR"
|
|
# Try parent of script directory
|
|
elif is_repo_root "$SCRIPT_DIR/.."; then
|
|
BUILD_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
fi
|
|
|
|
# If still not found, show error
|
|
if [[ -z "$BUILD_DIR" ]]; then
|
|
echo -e "${RED}Error: Cannot find atlas repository root${NC}"
|
|
echo " Current directory: $(pwd)"
|
|
echo " Script directory: $SCRIPT_DIR"
|
|
echo ""
|
|
echo " Looking for directory with:"
|
|
echo " - go.mod file"
|
|
echo " - internal/ directory"
|
|
echo ""
|
|
echo " Checking current directory:"
|
|
[[ -f "./go.mod" ]] && echo " ✓ Found go.mod" || echo " ✗ No go.mod"
|
|
[[ -d "./internal" ]] && echo " ✓ Found internal/" || echo " ✗ No internal/"
|
|
echo ""
|
|
echo " Please run installer from the atlas repository root"
|
|
echo " Or specify path with: --repo-dir /path/to/atlas"
|
|
exit 1
|
|
fi
|
|
|
|
cd "$BUILD_DIR"
|
|
echo "Building from: $(pwd)"
|
|
|
|
# Create cmd directory structure if it doesn't exist
|
|
if [[ ! -f "./cmd/atlas-api/main.go" ]]; then
|
|
echo -e "${YELLOW}cmd/ directory not found, creating...${NC}"
|
|
mkdir -p ./cmd/atlas-api
|
|
mkdir -p ./cmd/atlas-tui
|
|
|
|
# Create atlas-api/main.go
|
|
cat > ./cmd/atlas-api/main.go <<'MAINGO'
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"time"
|
|
|
|
"gitea.avt.data-center.id/othman.suseno/atlas/internal/httpapp"
|
|
tlscfg "gitea.avt.data-center.id/othman.suseno/atlas/internal/tls"
|
|
)
|
|
|
|
func main() {
|
|
addr := env("ATLAS_HTTP_ADDR", ":8080")
|
|
dbPath := env("ATLAS_DB_PATH", "data/atlas.db")
|
|
|
|
app, err := httpapp.New(httpapp.Config{
|
|
Addr: addr,
|
|
TemplatesDir: "web/templates",
|
|
StaticDir: "web/static",
|
|
DatabasePath: dbPath,
|
|
})
|
|
if err != nil {
|
|
log.Fatalf("init app: %v", err)
|
|
}
|
|
|
|
tlsConfig := tlscfg.LoadConfig()
|
|
if err := tlsConfig.Validate(); err != nil {
|
|
log.Fatalf("TLS configuration error: %v", err)
|
|
}
|
|
|
|
var tlsServerConfig interface{}
|
|
if tlsConfig.Enabled {
|
|
var err error
|
|
tlsServerConfig, err = tlsConfig.BuildTLSConfig()
|
|
if err != nil {
|
|
log.Fatalf("TLS configuration error: %v", err)
|
|
}
|
|
log.Printf("[atlas-api] TLS enabled (cert: %s, key: %s)", tlsConfig.CertFile, tlsConfig.KeyFile)
|
|
}
|
|
|
|
srv := &http.Server{
|
|
Addr: addr,
|
|
Handler: app.Router(),
|
|
ReadHeaderTimeout: 5 * time.Second,
|
|
WriteTimeout: 30 * time.Second,
|
|
IdleTimeout: 120 * time.Second,
|
|
}
|
|
|
|
if tlsServerConfig != nil {
|
|
if cfg, ok := tlsServerConfig.(*tls.Config); ok {
|
|
srv.TLSConfig = cfg
|
|
}
|
|
}
|
|
|
|
go func() {
|
|
log.Printf("[atlas-api] listening on %s", addr)
|
|
var err error
|
|
if tlsConfig.Enabled {
|
|
err = srv.ListenAndServeTLS("", "")
|
|
} else {
|
|
err = srv.ListenAndServe()
|
|
}
|
|
if err != nil && err != http.ErrServerClosed {
|
|
log.Fatalf("listen: %v", err)
|
|
}
|
|
}()
|
|
|
|
stop := make(chan os.Signal, 1)
|
|
signal.Notify(stop, os.Interrupt, os.Kill)
|
|
<-stop
|
|
log.Printf("[atlas-api] shutdown requested")
|
|
|
|
app.StopScheduler()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
if err := srv.Shutdown(ctx); err != nil {
|
|
log.Printf("[atlas-api] shutdown error: %v", err)
|
|
} else {
|
|
log.Printf("[atlas-api] shutdown complete")
|
|
}
|
|
}
|
|
|
|
func env(key, def string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return def
|
|
}
|
|
MAINGO
|
|
|
|
# Create atlas-tui/main.go
|
|
cat > ./cmd/atlas-tui/main.go <<'MAINGO'
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"gitea.avt.data-center.id/othman.suseno/atlas/internal/tui"
|
|
)
|
|
|
|
const (
|
|
defaultAPIURL = "http://localhost:8080"
|
|
)
|
|
|
|
func main() {
|
|
apiURL := os.Getenv("ATLAS_API_URL")
|
|
if apiURL == "" {
|
|
apiURL = defaultAPIURL
|
|
}
|
|
|
|
client := tui.NewAPIClient(apiURL)
|
|
app := tui.NewApp(client)
|
|
|
|
sigChan := make(chan os.Signal, 1)
|
|
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
|
go func() {
|
|
<-sigChan
|
|
app.Cleanup()
|
|
os.Exit(0)
|
|
}()
|
|
|
|
if err := app.Run(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
MAINGO
|
|
|
|
echo -e "${GREEN}Created cmd/ directory structure${NC}"
|
|
fi
|
|
|
|
# Verify cmd files exist now
|
|
if [[ ! -f "./cmd/atlas-api/main.go" ]]; then
|
|
echo -e "${RED}Error: Failed to create cmd/atlas-api/main.go${NC}"
|
|
exit 1
|
|
fi
|
|
if [[ ! -f "./cmd/atlas-tui/main.go" ]]; then
|
|
echo -e "${RED}Error: Failed to create cmd/atlas-tui/main.go${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# Build binaries
|
|
echo "Building atlas-api..."
|
|
if ! go build -o "$INSTALL_DIR/bin/atlas-api" ./cmd/atlas-api; then
|
|
echo -e "${RED}Error: Failed to build atlas-api${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
echo "Building atlas-tui..."
|
|
if ! go build -o "$INSTALL_DIR/bin/atlas-tui" ./cmd/atlas-tui; then
|
|
echo -e "${RED}Error: Failed to build atlas-tui${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# Set permissions
|
|
chown root:root "$INSTALL_DIR/bin/atlas-api"
|
|
chown root:root "$INSTALL_DIR/bin/atlas-tui"
|
|
chmod 755 "$INSTALL_DIR/bin/atlas-api"
|
|
chmod 755 "$INSTALL_DIR/bin/atlas-tui"
|
|
|
|
# Create symlinks in /usr/local/bin for global access
|
|
echo -e "${GREEN}Creating symlinks for global access...${NC}"
|
|
ln -sf "$INSTALL_DIR/bin/atlas-api" /usr/local/bin/atlas-api
|
|
ln -sf "$INSTALL_DIR/bin/atlas-tui" /usr/local/bin/atlas-tui
|
|
chmod 755 /usr/local/bin/atlas-api
|
|
chmod 755 /usr/local/bin/atlas-tui
|
|
|
|
echo -e "${GREEN}Binaries built and installed successfully${NC}"
|
|
echo " Binaries available at: $INSTALL_DIR/bin/"
|
|
echo " Global commands: atlas-api, atlas-tui"
|
|
}
|
|
|
|
# Create systemd service
|
|
create_systemd_service() {
|
|
echo -e "${GREEN}Creating systemd service...${NC}"
|
|
|
|
cat > /etc/systemd/system/atlas-api.service <<EOF
|
|
[Unit]
|
|
Description=AtlasOS Storage Controller API
|
|
After=network.target zfs.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=$SERVICE_USER
|
|
Group=$SERVICE_USER
|
|
WorkingDirectory=$INSTALL_DIR
|
|
ExecStart=$INSTALL_DIR/bin/atlas-api
|
|
Restart=always
|
|
RestartSec=10
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
SyslogIdentifier=atlas-api
|
|
|
|
# Environment variables
|
|
Environment="ATLAS_HTTP_ADDR=$HTTP_ADDR"
|
|
Environment="ATLAS_DB_PATH=$DB_PATH"
|
|
Environment="ATLAS_BACKUP_DIR=$BACKUP_DIR"
|
|
Environment="ATLAS_LOG_LEVEL=INFO"
|
|
Environment="ATLAS_LOG_FORMAT=json"
|
|
|
|
# Security
|
|
# Note: NoNewPrivileges is set to false to allow sudo for ZFS operations
|
|
# This is necessary for ZFS pool management
|
|
NoNewPrivileges=false
|
|
PrivateTmp=true
|
|
ProtectSystem=strict
|
|
ProtectHome=true
|
|
ReadWritePaths=$DATA_DIR $LOG_DIR $BACKUP_DIR $CONFIG_DIR
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
systemctl daemon-reload
|
|
echo -e "${GREEN}Systemd service created${NC}"
|
|
}
|
|
|
|
# Create configuration file
|
|
create_config() {
|
|
echo -e "${GREEN}Creating configuration...${NC}"
|
|
|
|
cat > "$CONFIG_DIR/atlas.conf" <<EOF
|
|
# AtlasOS Configuration
|
|
# This file is sourced by the systemd service
|
|
|
|
# HTTP Server
|
|
ATLAS_HTTP_ADDR=$HTTP_ADDR
|
|
|
|
# Database
|
|
ATLAS_DB_PATH=$DB_PATH
|
|
|
|
# Backup Directory
|
|
ATLAS_BACKUP_DIR=$BACKUP_DIR
|
|
|
|
# Logging
|
|
ATLAS_LOG_LEVEL=INFO
|
|
ATLAS_LOG_FORMAT=json
|
|
|
|
# TLS (optional - uncomment to enable)
|
|
# ATLAS_TLS_ENABLED=true
|
|
# ATLAS_TLS_CERT=$CONFIG_DIR/tls/cert.pem
|
|
# ATLAS_TLS_KEY=$CONFIG_DIR/tls/key.pem
|
|
|
|
# JWT Secret (generate with: openssl rand -hex 32)
|
|
# ATLAS_JWT_SECRET=your-secret-here
|
|
EOF
|
|
|
|
chown "$SERVICE_USER:$SERVICE_USER" "$CONFIG_DIR/atlas.conf"
|
|
chmod 600 "$CONFIG_DIR/atlas.conf"
|
|
|
|
echo -e "${GREEN}Configuration created${NC}"
|
|
}
|
|
|
|
# Generate JWT secret
|
|
generate_jwt_secret() {
|
|
echo -e "${GREEN}Generating JWT secret...${NC}"
|
|
|
|
if command -v openssl &> /dev/null; then
|
|
JWT_SECRET=$(openssl rand -hex 32)
|
|
echo "ATLAS_JWT_SECRET=$JWT_SECRET" >> "$CONFIG_DIR/atlas.conf"
|
|
echo -e "${GREEN}JWT secret generated${NC}"
|
|
else
|
|
echo -e "${YELLOW}Warning: openssl not found. Please set ATLAS_JWT_SECRET manually${NC}"
|
|
fi
|
|
}
|
|
|
|
# Configure firewall (UFW) for Ubuntu 24.04
|
|
configure_firewall() {
|
|
if [[ "$FIREWALL_ACTIVE" != "true" ]]; then
|
|
return
|
|
fi
|
|
|
|
echo -e "${GREEN}Configuring firewall rules...${NC}"
|
|
|
|
# Extract port from HTTP_ADDR (default :8080)
|
|
PORT=$(echo "$HTTP_ADDR" | sed 's/.*://' || echo "8080")
|
|
if [[ -z "$PORT" ]] || [[ "$PORT" == "$HTTP_ADDR" ]]; then
|
|
PORT="8080"
|
|
fi
|
|
|
|
# Allow HTTP port
|
|
echo " Allowing port $PORT for AtlasOS API..."
|
|
ufw allow "$PORT/tcp" comment "AtlasOS API" 2>/dev/null || true
|
|
|
|
# Allow SMB/CIFS ports
|
|
echo " Allowing SMB/CIFS ports..."
|
|
ufw allow 445/tcp comment "SMB/CIFS" 2>/dev/null || true
|
|
ufw allow 139/tcp comment "NetBIOS" 2>/dev/null || true
|
|
|
|
# Allow NFS ports
|
|
echo " Allowing NFS ports..."
|
|
ufw allow 2049/tcp comment "NFS" 2>/dev/null || true
|
|
ufw allow 2049/udp comment "NFS" 2>/dev/null || true
|
|
ufw allow 111/tcp comment "RPC" 2>/dev/null || true
|
|
ufw allow 111/udp comment "RPC" 2>/dev/null || true
|
|
|
|
# Allow iSCSI ports
|
|
echo " Allowing iSCSI ports..."
|
|
ufw allow 3260/tcp comment "iSCSI" 2>/dev/null || true
|
|
|
|
echo -e "${GREEN}Firewall configured${NC}"
|
|
echo ""
|
|
}
|
|
|
|
# Setup ZFS with comprehensive checks
|
|
setup_zfs() {
|
|
echo -e "${GREEN}Setting up ZFS...${NC}"
|
|
|
|
if ! command -v zpool &> /dev/null; then
|
|
echo -e "${RED}Error: ZFS utilities not found${NC}"
|
|
echo " Please install zfsutils-linux"
|
|
return 1
|
|
fi
|
|
|
|
# Load ZFS kernel module
|
|
echo " Loading ZFS kernel module..."
|
|
if ! lsmod | grep -q "^zfs"; then
|
|
modprobe zfs 2>/dev/null || {
|
|
echo -e "${YELLOW}Warning: Could not load ZFS module${NC}"
|
|
echo " This may require a system reboot"
|
|
}
|
|
else
|
|
echo -e "${GREEN} ✓ ZFS module loaded${NC}"
|
|
fi
|
|
|
|
# Ensure ZFS module loads on boot
|
|
if [[ ! -f /etc/modules-load.d/zfs.conf ]] || ! grep -q "^zfs" /etc/modules-load.d/zfs.conf 2>/dev/null; then
|
|
echo "zfs" > /etc/modules-load.d/zfs.conf
|
|
echo -e "${GREEN} ✓ ZFS will load on boot${NC}"
|
|
fi
|
|
|
|
# Check ZFS pool status
|
|
echo " Checking ZFS pools..."
|
|
if zpool list &>/dev/null; then
|
|
POOL_COUNT=$(zpool list -H | wc -l)
|
|
if [[ $POOL_COUNT -gt 0 ]]; then
|
|
echo -e "${GREEN} ✓ Found $POOL_COUNT ZFS pool(s)${NC}"
|
|
else
|
|
echo -e "${YELLOW} ⚠ No ZFS pools found (you can create them after installation)${NC}"
|
|
fi
|
|
else
|
|
echo -e "${YELLOW} ⚠ ZFS not fully initialized${NC}"
|
|
fi
|
|
|
|
echo -e "${GREEN}ZFS setup complete${NC}"
|
|
echo ""
|
|
}
|
|
|
|
# Setup Samba with dependency checks
|
|
setup_samba() {
|
|
echo -e "${GREEN}Setting up Samba...${NC}"
|
|
|
|
if ! command -v smbd &> /dev/null; then
|
|
echo -e "${RED}Error: Samba not found${NC}"
|
|
return 1
|
|
fi
|
|
|
|
# Create basic Samba config if it doesn't exist
|
|
if [[ ! -f /etc/samba/smb.conf ]] || [[ ! -s /etc/samba/smb.conf ]]; then
|
|
echo " Creating basic Samba configuration..."
|
|
mkdir -p /etc/samba
|
|
cat > /etc/samba/smb.conf <<'SAMBAEOF'
|
|
[global]
|
|
workgroup = WORKGROUP
|
|
server string = AtlasOS Storage Server
|
|
security = user
|
|
map to guest = Bad User
|
|
dns proxy = no
|
|
SAMBAEOF
|
|
echo -e "${GREEN} ✓ Basic Samba config created${NC}"
|
|
fi
|
|
|
|
# Enable and start Samba services
|
|
echo " Enabling Samba services..."
|
|
systemctl enable smbd 2>/dev/null || true
|
|
systemctl enable nmbd 2>/dev/null || true
|
|
|
|
# Start services if not running
|
|
if ! systemctl is-active --quiet smbd; then
|
|
systemctl start smbd 2>/dev/null || echo -e "${YELLOW} ⚠ Could not start smbd (may need manual start)${NC}"
|
|
fi
|
|
|
|
echo -e "${GREEN}Samba setup complete${NC}"
|
|
echo ""
|
|
}
|
|
|
|
# Setup NFS with dependency checks
|
|
setup_nfs() {
|
|
echo -e "${GREEN}Setting up NFS...${NC}"
|
|
|
|
if ! command -v exportfs &> /dev/null; then
|
|
echo -e "${RED}Error: NFS utilities not found${NC}"
|
|
return 1
|
|
fi
|
|
|
|
# Ensure /etc/exports exists
|
|
if [[ ! -f /etc/exports ]]; then
|
|
echo " Creating /etc/exports..."
|
|
touch /etc/exports
|
|
echo -e "${GREEN} ✓ /etc/exports created${NC}"
|
|
fi
|
|
|
|
# Enable and start NFS services
|
|
echo " Enabling NFS services..."
|
|
systemctl enable rpcbind 2>/dev/null || true
|
|
systemctl enable nfs-server 2>/dev/null || true
|
|
systemctl enable nfs-kernel-server 2>/dev/null || true
|
|
|
|
# Start rpcbind first (required dependency)
|
|
if ! systemctl is-active --quiet rpcbind; then
|
|
systemctl start rpcbind 2>/dev/null || echo -e "${YELLOW} ⚠ Could not start rpcbind${NC}"
|
|
fi
|
|
|
|
# Start NFS server
|
|
if ! systemctl is-active --quiet nfs-server && ! systemctl is-active --quiet nfs-kernel-server; then
|
|
systemctl start nfs-server 2>/dev/null || systemctl start nfs-kernel-server 2>/dev/null || \
|
|
echo -e "${YELLOW} ⚠ Could not start NFS server (may need manual start)${NC}"
|
|
fi
|
|
|
|
echo -e "${GREEN}NFS setup complete${NC}"
|
|
echo ""
|
|
}
|
|
|
|
# Setup iSCSI with dependency checks
|
|
setup_iscsi() {
|
|
echo -e "${GREEN}Setting up iSCSI...${NC}"
|
|
|
|
# Check for targetcli or targetcli-fb
|
|
TARGETCLI_CMD=""
|
|
if command -v targetcli &> /dev/null; then
|
|
TARGETCLI_CMD="targetcli"
|
|
elif command -v targetcli-fb &> /dev/null; then
|
|
TARGETCLI_CMD="targetcli-fb"
|
|
# Create symlink if targetcli doesn't exist
|
|
if ! command -v targetcli &> /dev/null; then
|
|
ln -sf $(which targetcli-fb) /usr/local/bin/targetcli 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
if [[ -z "$TARGETCLI_CMD" ]]; then
|
|
echo -e "${RED}Error: targetcli or targetcli-fb not found${NC}"
|
|
echo " Install with: apt-get install targetcli-fb"
|
|
return 1
|
|
fi
|
|
|
|
echo -e "${GREEN} ✓ Found $TARGETCLI_CMD${NC}"
|
|
|
|
# Check if target service exists
|
|
if systemctl list-unit-files | grep -q "^target.service"; then
|
|
# Enable and start iSCSI target service
|
|
echo " Enabling iSCSI target service..."
|
|
systemctl enable target 2>/dev/null || true
|
|
|
|
# Start service if not running
|
|
if ! systemctl is-active --quiet target; then
|
|
systemctl start target 2>/dev/null || echo -e "${YELLOW} ⚠ Could not start target service (may need manual start)${NC}"
|
|
fi
|
|
else
|
|
echo -e "${YELLOW} ⚠ target.service not found (LIO may not be properly installed)${NC}"
|
|
fi
|
|
|
|
echo -e "${GREEN}iSCSI setup complete (using $TARGETCLI_CMD)${NC}"
|
|
echo ""
|
|
}
|
|
|
|
# Create initial admin user
|
|
create_admin_user() {
|
|
echo -e "${GREEN}Creating initial admin user...${NC}"
|
|
|
|
echo ""
|
|
echo -e "${YELLOW}Please set up the initial admin user:${NC}"
|
|
echo " Username: admin"
|
|
echo " Password: (you will be prompted)"
|
|
echo ""
|
|
echo "After starting the service, you can create the admin user via:"
|
|
echo " curl -X POST http://localhost:8080/api/v1/users \\"
|
|
echo " -H 'Content-Type: application/json' \\"
|
|
echo " -d '{\"username\":\"admin\",\"password\":\"your-password\",\"role\":\"administrator\"}'"
|
|
echo ""
|
|
echo "Or use the TUI:"
|
|
echo " $INSTALL_DIR/bin/atlas-tui"
|
|
echo ""
|
|
}
|
|
|
|
# Start service
|
|
start_service() {
|
|
echo -e "${GREEN}Starting AtlasOS service...${NC}"
|
|
|
|
systemctl enable atlas-api
|
|
systemctl start atlas-api
|
|
|
|
# Wait a moment for service to start
|
|
sleep 2
|
|
|
|
if systemctl is-active --quiet atlas-api; then
|
|
echo -e "${GREEN}AtlasOS service started successfully${NC}"
|
|
else
|
|
echo -e "${RED}Error: Service failed to start${NC}"
|
|
echo "Check logs with: journalctl -u atlas-api -n 50"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Print summary
|
|
print_summary() {
|
|
echo ""
|
|
echo -e "${GREEN}========================================${NC}"
|
|
echo -e "${GREEN}AtlasOS Installation Complete!${NC}"
|
|
echo -e "${GREEN}Ubuntu 24.04 (Noble Numbat)${NC}"
|
|
echo -e "${GREEN}========================================${NC}"
|
|
echo ""
|
|
echo "Installation Directory: $INSTALL_DIR"
|
|
echo "Data Directory: $DATA_DIR"
|
|
echo "Config Directory: $CONFIG_DIR"
|
|
echo "Log Directory: $LOG_DIR"
|
|
echo ""
|
|
|
|
# Extract port from HTTP_ADDR
|
|
PORT=$(echo "$HTTP_ADDR" | sed 's/.*://' || echo "8080")
|
|
if [[ -z "$PORT" ]] || [[ "$PORT" == "$HTTP_ADDR" ]]; then
|
|
PORT="8080"
|
|
fi
|
|
|
|
echo "Service Status:"
|
|
systemctl status atlas-api --no-pager -l 2>/dev/null || echo " Service not running"
|
|
echo ""
|
|
|
|
echo "Useful Commands:"
|
|
echo " Service: systemctl {start|stop|restart|status} atlas-api"
|
|
echo " Logs: journalctl -u atlas-api -f"
|
|
echo " TUI: atlas-tui (or $INSTALL_DIR/bin/atlas-tui)"
|
|
echo ""
|
|
|
|
echo "Web Interface:"
|
|
echo " http://localhost:$PORT"
|
|
echo ""
|
|
|
|
echo "API Documentation:"
|
|
echo " http://localhost:$PORT/api/docs"
|
|
echo ""
|
|
|
|
if [[ "$FIREWALL_ACTIVE" == "true" ]]; then
|
|
echo -e "${YELLOW}Firewall Configuration:${NC}"
|
|
echo " UFW is active. The following ports have been allowed:"
|
|
echo " - Port $PORT (AtlasOS API)"
|
|
echo " - Port 445 (SMB/CIFS)"
|
|
echo " - Port 139 (NetBIOS)"
|
|
echo " - Port 2049 (NFS)"
|
|
echo " - Port 3260 (iSCSI)"
|
|
echo " To view rules: ufw status"
|
|
echo ""
|
|
fi
|
|
|
|
echo -e "${YELLOW}Storage Services Status:${NC}"
|
|
echo -n " ZFS: "
|
|
if command -v zpool &>/dev/null && lsmod | grep -q "^zfs"; then
|
|
echo -e "${GREEN}✓ Ready${NC}"
|
|
else
|
|
echo -e "${YELLOW}⚠ Check required${NC}"
|
|
fi
|
|
|
|
echo -n " Samba: "
|
|
if systemctl is-active --quiet smbd 2>/dev/null; then
|
|
echo -e "${GREEN}✓ Running${NC}"
|
|
else
|
|
echo -e "${YELLOW}⚠ Not running (start with: systemctl start smbd)${NC}"
|
|
fi
|
|
|
|
echo -n " NFS: "
|
|
if systemctl is-active --quiet nfs-server 2>/dev/null || systemctl is-active --quiet nfs-kernel-server 2>/dev/null; then
|
|
echo -e "${GREEN}✓ Running${NC}"
|
|
else
|
|
echo -e "${YELLOW}⚠ Not running (start with: systemctl start nfs-server)${NC}"
|
|
fi
|
|
|
|
echo -n " iSCSI: "
|
|
if systemctl is-active --quiet target 2>/dev/null; then
|
|
echo -e "${GREEN}✓ Running${NC}"
|
|
else
|
|
echo -e "${YELLOW}⚠ Not running (start with: systemctl start target)${NC}"
|
|
fi
|
|
echo ""
|
|
|
|
echo -e "${YELLOW}Next Steps:${NC}"
|
|
echo "1. Create initial admin user (see instructions above)"
|
|
echo "2. Configure TLS certificates (optional) - see $CONFIG_DIR/atlas.conf"
|
|
echo "3. Review configuration in $CONFIG_DIR/atlas.conf"
|
|
echo "4. Ensure storage services are running if needed"
|
|
echo ""
|
|
|
|
echo -e "${GREEN}Installation completed successfully!${NC}"
|
|
echo ""
|
|
}
|
|
|
|
# Main installation
|
|
main() {
|
|
echo -e "${GREEN}========================================${NC}"
|
|
echo -e "${GREEN}AtlasOS Installation Script${NC}"
|
|
echo -e "${GREEN}For Ubuntu 24.04 (Noble Numbat)${NC}"
|
|
echo -e "${GREEN}========================================${NC}"
|
|
echo ""
|
|
|
|
# Step 1: Detect and validate distribution
|
|
detect_distro
|
|
echo ""
|
|
|
|
# Step 2: Pre-flight checks
|
|
preflight_checks
|
|
|
|
# Step 3: Fix infrastructure gaps
|
|
fix_infrastructure_gaps
|
|
|
|
# Step 4: Install dependencies
|
|
if [[ "$SKIP_DEPS" == "false" ]]; then
|
|
install_dependencies
|
|
else
|
|
echo -e "${YELLOW}Skipping dependency installation${NC}"
|
|
echo ""
|
|
fi
|
|
|
|
# Step 5: Create system user and directories
|
|
create_user
|
|
create_directories
|
|
|
|
# Step 6: Build binaries
|
|
build_binaries
|
|
|
|
# Step 7: Create configuration
|
|
create_config
|
|
generate_jwt_secret
|
|
|
|
# Step 8: Create systemd service
|
|
create_systemd_service
|
|
|
|
# Step 9: Setup storage services
|
|
setup_zfs
|
|
setup_samba
|
|
setup_nfs
|
|
setup_iscsi
|
|
|
|
# Step 10: Configure firewall
|
|
configure_firewall
|
|
|
|
# Step 11: Create admin user info
|
|
create_admin_user
|
|
|
|
# Step 12: Ask if user wants to start service
|
|
echo ""
|
|
read -p "Start AtlasOS service now? (y/n) " -n 1 -r
|
|
echo ""
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
start_service
|
|
else
|
|
echo -e "${YELLOW}Service not started. Start manually with: systemctl start atlas-api${NC}"
|
|
fi
|
|
|
|
# Step 13: Print summary
|
|
print_summary
|
|
}
|
|
|
|
# Run main
|
|
main
|