new binary mode and systemd process

This commit is contained in:
Ben Duncan
2026-03-16 11:57:52 +00:00
parent e7a3b5b712
commit a458a28406
19 changed files with 774 additions and 33 deletions
+44
View File
@@ -0,0 +1,44 @@
name: Release
on:
push:
tags: ['v*']
permissions:
contents: write
jobs:
distro:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
path: hive
- uses: actions/checkout@v4
with:
repository: mulgadc/predastore
path: predastore
- uses: actions/checkout@v4
with:
repository: mulgadc/viperblock
path: viperblock
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set up QEMU (for arm64 cross-build)
uses: docker/setup-qemu-action@v3
- name: Build distribution tarballs
run: cd hive && make distro VERSION=${{ github.ref_name }}
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: |
hive/dist/*.tar.gz
hive/dist/*.sha256
hive/scripts/setup.sh
generate_release_notes: true
+1
View File
@@ -13,6 +13,7 @@ go.work.sum
# Build outputs
bin/
dist/
tmp/
*.test
*.out
+38 -2
View File
@@ -55,9 +55,13 @@ build-ui:
cd hive/services/hiveui/frontend && pnpm build
# GO commands
VERSION ?= $(shell git describe --tags --always --dirty)
COMMIT ?= $(shell git rev-parse --short HEAD)
LDFLAGS := -s -w -X github.com/mulgadc/hive/cmd/hive/cmd.Version=$(VERSION) -X github.com/mulgadc/hive/cmd/hive/cmd.Commit=$(COMMIT)
go_build:
@echo -e "\n....Building $(GO_PROJECT_NAME)"
go build $(GO_BUILD_MOD) -ldflags "-s -w" -o ./bin/$(GO_PROJECT_NAME) cmd/hive/main.go
go build $(GO_BUILD_MOD) -ldflags "$(LDFLAGS)" -o ./bin/$(GO_PROJECT_NAME) cmd/hive/main.go
go_run:
@echo -e "\n....Running $(GO_PROJECT_NAME)...."
@@ -193,6 +197,38 @@ security-check:
$(_Q)set -o pipefail && go tool staticcheck -checks="all,-ST1000,-ST1003,-ST1016,-ST1020,-ST1021,-ST1022,-SA1019,-SA9005" ./... $(_SECQ) tests/staticcheck-report.txt $(if $(QUIET),|| { cat tests/staticcheck-report.txt; exit 1; })
@echo " staticcheck ok"
# Build release tarballs for both architectures via Docker
distro:
@echo "Building hive $(VERSION) distribution tarballs..."
@mkdir -p dist/
@echo "Building linux/amd64..."
docker buildx build \
--platform linux/amd64 \
--build-arg VERSION=$(VERSION) \
-f build/Dockerfile.distro \
--output type=local,dest=dist/amd64/ \
../
tar -czf dist/hive-$(VERSION)-linux-amd64.tar.gz -C dist/amd64 .
sha256sum dist/hive-$(VERSION)-linux-amd64.tar.gz > dist/hive-$(VERSION)-linux-amd64.tar.gz.sha256
@echo "Building linux/arm64..."
docker buildx build \
--platform linux/arm64 \
--build-arg VERSION=$(VERSION) \
-f build/Dockerfile.distro \
--output type=local,dest=dist/arm64/ \
../
tar -czf dist/hive-$(VERSION)-linux-arm64.tar.gz -C dist/arm64 .
sha256sum dist/hive-$(VERSION)-linux-arm64.tar.gz > dist/hive-$(VERSION)-linux-arm64.tar.gz.sha256
@echo ""
@echo "Distribution tarballs:"
@ls -lh dist/*.tar.gz
@echo ""
@cat dist/*.sha256
distro-clean:
rm -rf dist/
.PHONY: build build-ui go_build go_run preflight test test-cover test-race diff-coverage bench run clean \
install-system install-go install-aws quickinstall \
format check-format modernize check-modernize vet security-check
format check-format modernize check-modernize vet security-check \
distro distro-clean
+48
View File
@@ -0,0 +1,48 @@
ARG VERSION=dev
FROM golang:1.26-bookworm AS builder
ARG VERSION
RUN apt-get update && apt-get install -y --no-install-recommends \
nbdkit nbdkit-plugin-dev pkg-config gcc make \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /build
# Copy go.work first (lives in hive/)
COPY hive/go.work ./hive/
# Copy module files for dependency caching
COPY hive/go.mod hive/go.sum ./hive/
COPY predastore/go.mod predastore/go.sum ./predastore/
COPY viperblock/go.mod viperblock/go.sum ./viperblock/
COPY viperblock/nbd/go.mod viperblock/nbd/go.sum ./viperblock/nbd/
COPY viperblock/nbd/libguestfs.org/nbdkit/go.mod ./viperblock/nbd/libguestfs.org/nbdkit/
# Download dependencies (cached layer)
RUN cd hive && go mod download
# Copy full source
COPY hive/ ./hive/
COPY predastore/ ./predastore/
COPY viperblock/ ./viperblock/
# Build hive binary
RUN COMMIT=$(cd hive && git rev-parse --short HEAD 2>/dev/null || echo "unknown") && \
cd hive && go build -ldflags "-s -w \
-X github.com/mulgadc/hive/cmd/hive/cmd.Version=${VERSION} \
-X github.com/mulgadc/hive/cmd/hive/cmd.Commit=${COMMIT}" \
-o /out/hive cmd/hive/main.go
# Build nbdkit viperblock plugin (.so)
RUN cd viperblock && go build -o /out/nbdkit-viperblock-plugin.so -buildmode=c-shared nbd/viperblock.go
# Copy setup scripts and systemd units
RUN cp hive/scripts/setup.sh /out/setup.sh 2>/dev/null || true
RUN cp hive/scripts/setup-ovn.sh /out/setup-ovn.sh
RUN mkdir -p /out/systemd && cp hive/build/systemd/* /out/systemd/
RUN cp hive/build/logrotate/hive /out/logrotate-hive
FROM scratch AS export
COPY --from=builder /out/ /
+9
View File
@@ -0,0 +1,9 @@
/var/log/hive/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
copytruncate
}
+22
View File
@@ -0,0 +1,22 @@
[Unit]
Description=Hive AWS Gateway
PartOf=hive.target
After=hive-nats.service hive-daemon.service
Requires=hive-nats.service
[Service]
Type=simple
User=hive
Group=hive
ExecStartPre=/bin/sh -c 'for i in $(seq 1 30); do nc -z 127.0.0.1 4222 && exit 0; sleep 1; done; exit 1'
ExecStart=/usr/local/bin/hive service awsgw start
Restart=on-failure
RestartSec=5
OOMScoreAdjust=-500
Environment=HIVE_CONFIG_PATH=/etc/hive/hive.toml
Environment=HIVE_BASE_DIR=/var/lib/hive/
Environment=HIVE_AWSGW_TLS_CERT=/etc/hive/server.pem
Environment=HIVE_AWSGW_TLS_KEY=/etc/hive/server.key
[Install]
WantedBy=hive.target
+21
View File
@@ -0,0 +1,21 @@
[Unit]
Description=Hive Daemon (VM Orchestration)
PartOf=hive.target
After=hive-nats.service hive-viperblock.service
Requires=hive-nats.service
[Service]
Type=simple
User=hive
Group=hive
ExecStartPre=/bin/sh -c 'for i in $(seq 1 30); do nc -z 127.0.0.1 4222 && exit 0; sleep 1; done; exit 1'
ExecStart=/usr/local/bin/hive service hive start
Restart=on-failure
RestartSec=5
OOMScoreAdjust=-500
Environment=HIVE_CONFIG_PATH=/etc/hive/hive.toml
Environment=HIVE_BASE_DIR=/var/lib/hive/hive/
Environment=HIVE_WAL_DIR=/var/lib/hive/hive/
[Install]
WantedBy=hive.target
+17
View File
@@ -0,0 +1,17 @@
[Unit]
Description=Hive NATS Message Bus
PartOf=hive.target
Before=hive-predastore.service hive-daemon.service hive-awsgw.service hive-vpcd.service
[Service]
Type=simple
User=hive
Group=hive
ExecStart=/usr/local/bin/hive service nats start
Restart=on-failure
RestartSec=5
OOMScoreAdjust=-500
Environment=HIVE_CONFIG_PATH=/etc/hive/nats/nats.conf
[Install]
WantedBy=hive.target
+22
View File
@@ -0,0 +1,22 @@
[Unit]
Description=Hive Predastore (S3-compatible Object Storage)
PartOf=hive.target
After=hive-nats.service
Requires=hive-nats.service
[Service]
Type=simple
User=hive
Group=hive
ExecStartPre=/bin/sh -c 'for i in $(seq 1 30); do nc -z 127.0.0.1 4222 && exit 0; sleep 1; done; exit 1'
ExecStart=/usr/local/bin/hive service predastore start
Restart=on-failure
RestartSec=5
OOMScoreAdjust=-500
Environment=HIVE_PREDASTORE_CONFIG_PATH=/etc/hive/predastore/predastore.toml
Environment=HIVE_PREDASTORE_BASE_PATH=/var/lib/hive/predastore/
Environment=HIVE_PREDASTORE_TLS_CERT=/etc/hive/server.pem
Environment=HIVE_PREDASTORE_TLS_KEY=/etc/hive/server.key
[Install]
WantedBy=hive.target
+16
View File
@@ -0,0 +1,16 @@
[Unit]
Description=Hive Web UI
PartOf=hive.target
After=hive-awsgw.service
[Service]
Type=simple
User=hive
Group=hive
ExecStart=/usr/local/bin/hive service hive-ui start
Restart=on-failure
RestartSec=5
OOMScoreAdjust=-500
[Install]
WantedBy=hive.target
+21
View File
@@ -0,0 +1,21 @@
[Unit]
Description=Hive Viperblock (Block Storage)
PartOf=hive.target
After=hive-nats.service hive-predastore.service
Requires=hive-nats.service
[Service]
Type=simple
User=hive
Group=hive
ExecStartPre=/bin/sh -c 'for i in $(seq 1 30); do nc -z 127.0.0.1 4222 && exit 0; sleep 1; done; exit 1'
ExecStart=/usr/local/bin/hive service viperblock start
Restart=on-failure
RestartSec=5
OOMScoreAdjust=-500
EnvironmentFile=/etc/hive/systemd.env
Environment=HIVE_CONFIG_PATH=/etc/hive/hive.toml
Environment=HIVE_BASE_DIR=/var/lib/hive/viperblock/
[Install]
WantedBy=hive.target
+19
View File
@@ -0,0 +1,19 @@
[Unit]
Description=Hive VPC Daemon (OVN Networking)
PartOf=hive.target
After=hive-nats.service hive-daemon.service
Requires=hive-nats.service
[Service]
Type=simple
User=hive
Group=hive
ExecStartPre=/bin/sh -c 'for i in $(seq 1 30); do nc -z 127.0.0.1 4222 && exit 0; sleep 1; done; exit 1'
ExecStart=/usr/local/bin/hive service vpcd start
Restart=on-failure
RestartSec=5
OOMScoreAdjust=-500
Environment=HIVE_CONFIG_PATH=/etc/hive/hive.toml
[Install]
WantedBy=hive.target
+8
View File
@@ -0,0 +1,8 @@
[Unit]
Description=Hive Infrastructure Platform
After=network-online.target
Wants=network-online.target
Wants=hive-nats.service hive-predastore.service hive-viperblock.service hive-daemon.service hive-awsgw.service hive-vpcd.service hive-ui.service
[Install]
WantedBy=multi-user.target
+17 -29
View File
@@ -196,12 +196,8 @@ func init() {
accountCreateCmd.Flags().String("name", "", "Account name (required)")
accountCreateCmd.MarkFlagRequired("name")
homeDir, _ := os.UserHomeDir()
configDir := filepath.Join(homeDir, "hive", "config")
hiveDir := filepath.Join(homeDir, "hive")
rootCmd.PersistentFlags().String("config-dir", configDir, "Configuration directory")
rootCmd.PersistentFlags().String("hive-dir", hiveDir, "Hive base directory")
rootCmd.PersistentFlags().String("config-dir", DefaultConfigDir(), "Configuration directory")
rootCmd.PersistentFlags().String("hive-dir", DefaultDataDir(), "Hive base directory")
// Flags for admin init
adminInitCmd.Flags().Bool("force", false, "Force re-initialization (overwrites existing config)")
@@ -259,8 +255,7 @@ func runimagesImportCmd(cmd *cobra.Command, args []string) {
// Use default config path
if cfgFile == "" {
homeDir, _ := os.UserHomeDir()
cfgFile = fmt.Sprintf("%s/hive/config/hive.toml", homeDir)
cfgFile = DefaultConfigFile()
}
//configDir, _ := cmd.Flags().GetString("config-dir")
@@ -566,12 +561,7 @@ func runAdminInit(cmd *cobra.Command, args []string) {
// Default config directory
if configDir == "" {
homeDir, err := os.UserHomeDir()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting home directory: %v\n", err)
os.Exit(1)
}
configDir = filepath.Join(homeDir, "hive", "config")
configDir = DefaultConfigDir()
}
fmt.Println("🚀 Initializing Hive platform...")
@@ -640,9 +630,7 @@ func runAdminInit(cmd *cobra.Command, args []string) {
fmt.Println("\n🔒 Generated NATS authentication token")
if hiveRoot == "" {
// Get home directory for data path
homeDir, _ := os.UserHomeDir()
hiveRoot = filepath.Join(homeDir, "hive")
hiveRoot = DefaultDataDir()
}
hiveRoot = filepath.Clean(hiveRoot)
@@ -768,6 +756,16 @@ func runAdminInit(cmd *cobra.Command, args []string) {
admin.CreateServiceDirectories(hiveRoot)
// In production layout (running as root), fix ownership so the service user
// can read config and write data. SUDO_USER identifies the operator account.
if os.Getuid() == 0 {
sudoUser := os.Getenv("SUDO_USER")
if sudoUser != "" {
admin.ChownRecursive(configDir, sudoUser)
admin.ChownRecursive(hiveRoot, sudoUser)
}
}
// Print success message
fmt.Println("\n🎉 Hive initialization complete!")
fmt.Println("\n📋 Next steps:")
@@ -1028,12 +1026,7 @@ func runAdminJoin(cmd *cobra.Command, args []string) {
// Set default data directory
if dataDir == "" {
homeDir, err := os.UserHomeDir()
if err != nil {
fmt.Fprintf(os.Stderr, "❌ Error getting home directory: %v\n", err)
os.Exit(1)
}
dataDir = filepath.Join(homeDir, "hive")
dataDir = DefaultDataDir()
}
fmt.Println("🚀 Joining Hive cluster...")
@@ -1134,12 +1127,7 @@ func runAdminJoin(cmd *cobra.Command, args []string) {
// Set up config directory
if configDir == "" {
homeDir, err := os.UserHomeDir()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting home directory: %v\n", err)
os.Exit(1)
}
configDir = filepath.Join(homeDir, "hive", "config")
configDir = DefaultConfigDir()
}
if err := os.MkdirAll(configDir, 0700); err != nil {
+1 -2
View File
@@ -66,8 +66,7 @@ func init() {
func loadConfigAndConnect() (*config.ClusterConfig, *nats.Conn, error) {
cfgPath := viper.GetString("config")
if cfgPath == "" {
homeDir, _ := os.UserHomeDir()
cfgPath = fmt.Sprintf("%s/hive/config/hive.toml", homeDir)
cfgPath = DefaultConfigFile()
}
cfg, err := config.LoadConfig(cfgPath)
+44
View File
@@ -0,0 +1,44 @@
package cmd
import (
"os"
"path/filepath"
)
// DefaultConfigDir returns the default configuration directory.
// Production: /etc/hive (when running as root or /etc/hive exists)
// Development: ~/hive/config
func DefaultConfigDir() string {
if isProductionLayout() {
return "/etc/hive"
}
homeDir, _ := os.UserHomeDir()
return filepath.Join(homeDir, "hive", "config")
}
// DefaultDataDir returns the default data directory.
// Production: /var/lib/hive
// Development: ~/hive
func DefaultDataDir() string {
if isProductionLayout() {
return "/var/lib/hive"
}
homeDir, _ := os.UserHomeDir()
return filepath.Join(homeDir, "hive")
}
// DefaultConfigFile returns the default path to hive.toml.
func DefaultConfigFile() string {
return filepath.Join(DefaultConfigDir(), "hive.toml")
}
// isProductionLayout returns true when running in a production install.
// Detected by: running as root, or /etc/hive directory exists.
func isProductionLayout() bool {
if os.Getuid() == 0 {
if info, err := os.Stat("/etc/hive"); err == nil && info.IsDir() {
return true
}
}
return false
}
+27
View File
@@ -0,0 +1,27 @@
package cmd
import (
"fmt"
"runtime"
"github.com/spf13/cobra"
)
// Version variables set via ldflags at build time.
// Example: go build -ldflags "-X github.com/mulgadc/hive/cmd/hive/cmd.Version=v1.0.0"
var (
Version = "dev"
Commit = "unknown"
)
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the Hive version",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("hive %s (%s) %s/%s\n", Version, Commit, runtime.GOOS, runtime.GOARCH)
},
}
func init() {
rootCmd.AddCommand(versionCmd)
}
+23
View File
@@ -8,11 +8,14 @@ import (
"encoding/base64"
"encoding/pem"
"fmt"
"io/fs"
"log/slog"
"math/big"
"net"
"os"
"os/user"
"path/filepath"
"strconv"
"strings"
"text/template"
"time"
@@ -205,6 +208,26 @@ func FileExists(path string) bool {
return err == nil
}
// ChownRecursive changes ownership of a path and all its contents to the
// specified username. Used after init to hand production directories back
// to the service user when init runs as root via sudo.
func ChownRecursive(path, username string) {
u, err := user.Lookup(username)
if err != nil {
return
}
uid, _ := strconv.Atoi(u.Uid)
gid, _ := strconv.Atoi(u.Gid)
filepath.WalkDir(path, func(p string, d fs.DirEntry, err error) error {
if err != nil {
return nil
}
os.Chown(p, uid, gid)
return nil
})
}
// updateAWSINIFile updates or creates an AWS INI file section with given key-value pairs
func UpdateAWSINIFile(path, section string, values map[string]string) error {
var cfg *ini.File
+376
View File
@@ -0,0 +1,376 @@
#!/bin/bash
# Hive binary installer
# Usage: curl -sfL https://install.mulgadc.com/setup.sh | bash
#
# Environment variables:
# INSTALL_HIVE_CHANNEL Release channel: latest (default), dev
# INSTALL_HIVE_VERSION Pin to specific version (overrides channel)
# INSTALL_HIVE_TARBALL Path to local tarball (skips download, for testing/air-gapped)
# INSTALL_HIVE_SKIP_APT Set to 1 to skip apt dependency install
# INSTALL_HIVE_SKIP_AWS Set to 1 to skip AWS CLI install
set -e
INSTALL_HIVE_CHANNEL="${INSTALL_HIVE_CHANNEL:-latest}"
INSTALL_BASE_URL="${INSTALL_BASE_URL:-https://install.mulgadc.com}"
# --- Colors ---
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
fatal() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
# --- Root check ---
check_root() {
if [ "$(id -u)" -ne 0 ]; then
fatal "This script must be run as root. Use: curl -sfL ... | sudo bash"
fi
}
# --- OS detection ---
detect_os() {
if [ ! -f /etc/os-release ]; then
fatal "Cannot detect OS: /etc/os-release not found"
fi
. /etc/os-release
case "$ID" in
debian)
if [ "${VERSION_ID%%.*}" -lt 12 ] 2>/dev/null; then
fatal "Debian $VERSION_ID is not supported. Minimum: Debian 12"
fi
;;
ubuntu)
major="${VERSION_ID%%.*}"
if [ "$major" -lt 22 ] 2>/dev/null; then
fatal "Ubuntu $VERSION_ID is not supported. Minimum: Ubuntu 22.04"
fi
;;
*)
fatal "Unsupported OS: $ID $VERSION_ID. Hive requires Debian 12+ or Ubuntu 22.04+"
;;
esac
info "Detected OS: $PRETTY_NAME"
}
# --- Architecture detection ---
detect_arch() {
MACHINE=$(uname -m)
case "$MACHINE" in
x86_64)
ARCH="amd64"
QEMU_PACKAGES="qemu-system-x86"
AWS_ARCH="x86_64"
;;
aarch64|arm64)
ARCH="arm64"
QEMU_PACKAGES="qemu-system-arm"
AWS_ARCH="aarch64"
;;
*)
fatal "Unsupported architecture: $MACHINE. Hive requires x86_64 or aarch64"
;;
esac
info "Detected architecture: $MACHINE ($ARCH)"
}
# --- Detect service user ---
detect_service_user() {
# Use the real user who invoked sudo (not root)
HIVE_USER="${SUDO_USER:-$(whoami)}"
HIVE_GROUP="$(id -gn "$HIVE_USER")"
if [ "$HIVE_USER" = "root" ]; then
fatal "Cannot determine service user. Do not run as root directly — use: sudo bash setup.sh"
fi
info "Services will run as: $HIVE_USER:$HIVE_GROUP"
# Allow the service user to manage OVN/OVS and tap devices
cat > /etc/sudoers.d/hive-network << SUDOERS
# Hive VPC networking: allow service user to manage tap devices, OVS, and OVN
${HIVE_USER} ALL=(root) NOPASSWD: /sbin/ip, /usr/sbin/ip
${HIVE_USER} ALL=(root) NOPASSWD: /usr/bin/ovs-vsctl, /usr/bin/ovs-appctl
${HIVE_USER} ALL=(root) NOPASSWD: /usr/bin/ovn-nbctl, /usr/bin/ovn-sbctl
SUDOERS
chmod 0440 /etc/sudoers.d/hive-network
info "Sudoers rules installed for $HIVE_USER"
}
# --- Install apt dependencies ---
install_apt_deps() {
if [ "${INSTALL_HIVE_SKIP_APT}" = "1" ]; then
info "Skipping apt dependencies (INSTALL_HIVE_SKIP_APT=1)"
return
fi
info "Installing system dependencies..."
apt-get update -qq
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
nbdkit \
$QEMU_PACKAGES qemu-utils qemu-kvm \
libvirt-daemon-system libvirt-clients \
jq curl iproute2 netcat-openbsd wget unzip xz-utils file \
ovn-central ovn-host openvswitch-switch \
> /dev/null
info "System dependencies installed"
}
# --- Install AWS CLI ---
install_aws_cli() {
if [ "${INSTALL_HIVE_SKIP_AWS}" = "1" ]; then
info "Skipping AWS CLI (INSTALL_HIVE_SKIP_AWS=1)"
return
fi
if command -v aws >/dev/null 2>&1; then
info "AWS CLI already installed: $(aws --version 2>&1 | head -1)"
return
fi
info "Installing AWS CLI v2..."
AWS_TMPDIR=$(mktemp -d)
curl -fsSL "https://awscli.amazonaws.com/awscli-exe-linux-${AWS_ARCH}.zip" -o "$AWS_TMPDIR/awscliv2.zip"
unzip -q "$AWS_TMPDIR/awscliv2.zip" -d "$AWS_TMPDIR"
"$AWS_TMPDIR/aws/install" --update > /dev/null
rm -rf "$AWS_TMPDIR"
info "AWS CLI installed: $(aws --version 2>&1 | head -1)"
}
# --- Download tarball ---
download_hive() {
HIVE_TMPDIR=$(mktemp -d)
TARBALL="$HIVE_TMPDIR/hive.tar.gz"
# Local tarball override — skip download (for testing and air-gapped installs)
if [ -n "$INSTALL_HIVE_TARBALL" ]; then
info "Using local tarball: $INSTALL_HIVE_TARBALL"
cp "$INSTALL_HIVE_TARBALL" "$TARBALL"
info "Extracting..."
tar -xzf "$TARBALL" -C "$HIVE_TMPDIR"
EXTRACT_DIR="$HIVE_TMPDIR"
return
fi
if [ -n "$INSTALL_HIVE_VERSION" ]; then
DOWNLOAD_URL="${INSTALL_BASE_URL}/download/${INSTALL_HIVE_VERSION}/${ARCH}"
info "Downloading Hive $INSTALL_HIVE_VERSION ($ARCH)..."
else
DOWNLOAD_URL="${INSTALL_BASE_URL}/download/${INSTALL_HIVE_CHANNEL}/${ARCH}"
info "Downloading Hive ($INSTALL_HIVE_CHANNEL channel, $ARCH)..."
fi
HTTP_CODE=$(curl -fsSL -w '%{http_code}' -o "$TARBALL" "$DOWNLOAD_URL" 2>/dev/null) || true
if [ ! -f "$TARBALL" ] || [ "$HTTP_CODE" -ge 400 ] 2>/dev/null; then
rm -rf "$HIVE_TMPDIR"
fatal "Failed to download Hive from $DOWNLOAD_URL (HTTP $HTTP_CODE)"
fi
# Verify checksum if available
CHECKSUM_URL="${DOWNLOAD_URL}.sha256"
if curl -fsSL -o "$HIVE_TMPDIR/checksum.sha256" "$CHECKSUM_URL" 2>/dev/null; then
info "Verifying checksum..."
EXPECTED=$(awk '{print $1}' "$HIVE_TMPDIR/checksum.sha256")
ACTUAL=$(sha256sum "$TARBALL" | awk '{print $1}')
if [ "$EXPECTED" != "$ACTUAL" ]; then
rm -rf "$HIVE_TMPDIR"
fatal "Checksum verification failed. Expected: $EXPECTED, Got: $ACTUAL"
fi
info "Checksum verified"
else
warn "Checksum not available, skipping verification"
fi
# Extract
info "Extracting..."
tar -xzf "$TARBALL" -C "$HIVE_TMPDIR"
EXTRACT_DIR="$HIVE_TMPDIR"
}
# --- Place files ---
install_files() {
info "Installing files..."
# Binary
install -m 0755 "$EXTRACT_DIR/hive" /usr/local/bin/hive
info " /usr/local/bin/hive"
# nbdkit plugin
PLUGINDIR=$(nbdkit --dump-config 2>/dev/null | grep ^plugindir= | cut -d= -f2)
if [ -z "$PLUGINDIR" ]; then
warn "Could not detect nbdkit plugin directory, using default"
if [ "$ARCH" = "arm64" ]; then
PLUGINDIR="/usr/lib/aarch64-linux-gnu/nbdkit/plugins"
else
PLUGINDIR="/usr/lib/x86_64-linux-gnu/nbdkit/plugins"
fi
fi
mkdir -p "$PLUGINDIR"
install -m 0755 "$EXTRACT_DIR/nbdkit-viperblock-plugin.so" "$PLUGINDIR/nbdkit-viperblock-plugin.so"
info " $PLUGINDIR/nbdkit-viperblock-plugin.so"
# Setup scripts
mkdir -p /usr/local/share/hive
if [ -f "$EXTRACT_DIR/setup-ovn.sh" ]; then
install -m 0755 "$EXTRACT_DIR/setup-ovn.sh" /usr/local/share/hive/setup-ovn.sh
info " /usr/local/share/hive/setup-ovn.sh"
fi
# Cleanup temp dir after all install steps complete (defer to main)
}
# --- Create directories ---
create_directories() {
info "Creating directories..."
mkdir -p /etc/hive
chmod 0700 /etc/hive
chown "$HIVE_USER:$HIVE_GROUP" /etc/hive
mkdir -p /var/lib/hive
chown "$HIVE_USER:$HIVE_GROUP" /var/lib/hive
# Symlink so services that expect BaseDir/config/ can find /etc/hive/
if [ ! -e /var/lib/hive/config ]; then
ln -s /etc/hive /var/lib/hive/config
fi
# Symlink so services that write logs to BaseDir/logs/ use /var/log/hive/
if [ ! -e /var/lib/hive/logs ]; then
ln -s /var/log/hive /var/lib/hive/logs
fi
mkdir -p /var/log/hive
chown "$HIVE_USER:$HIVE_GROUP" /var/log/hive
mkdir -p /run/hive
chown "$HIVE_USER:$HIVE_GROUP" /run/hive
mkdir -p /var/lib/hive/viperblock
# Generate environment file with install-specific values (e.g. arch-dependent paths)
cat > /etc/hive/systemd.env << EOF
# Generated by setup.sh — install-specific environment variables
HIVE_VIPERBLOCK_PLUGIN_PATH=${PLUGINDIR}/nbdkit-viperblock-plugin.so
EOF
chown "$HIVE_USER:$HIVE_GROUP" /etc/hive/systemd.env
info "Generated /etc/hive/systemd.env"
}
# --- Install systemd units ---
install_systemd() {
info "Installing systemd units..."
if [ ! -d "$EXTRACT_DIR/systemd" ]; then
fatal "Systemd unit files not found in tarball (expected systemd/ directory)"
fi
for unit in "$EXTRACT_DIR"/systemd/*; do
# Substitute User= and Group= with the detected service user
sed "s/^User=hive$/User=$HIVE_USER/;s/^Group=hive$/Group=$HIVE_GROUP/" \
"$unit" > "/etc/systemd/system/$(basename "$unit")"
chmod 0644 "/etc/systemd/system/$(basename "$unit")"
info " /etc/systemd/system/$(basename "$unit")"
done
systemctl daemon-reload
info "Systemd units installed (running as $HIVE_USER:$HIVE_GROUP)"
}
# --- Install logrotate ---
install_logrotate() {
if [ -f "$EXTRACT_DIR/logrotate-hive" ]; then
install -m 0644 "$EXTRACT_DIR/logrotate-hive" /etc/logrotate.d/hive
else
warn "Logrotate config not found in tarball, skipping"
return
fi
info "Logrotate config installed"
}
# --- Upgrade handling ---
handle_upgrade() {
if systemctl is-active --quiet hive.target 2>/dev/null; then
warn "Hive services are running. Stopping for upgrade..."
systemctl stop hive.target
RESTART_AFTER=true
fi
}
restart_if_needed() {
if [ "${RESTART_AFTER}" = "true" ]; then
info "Restarting Hive services..."
systemctl start hive.target
fi
}
# --- Print summary ---
print_summary() {
INSTALLED_VERSION=$(/usr/local/bin/hive version 2>/dev/null || echo "unknown")
echo ""
echo "============================================"
echo " Hive installed successfully"
echo "============================================"
echo ""
echo " Version: $INSTALLED_VERSION"
echo " Architecture: $ARCH"
echo " Binary: /usr/local/bin/hive"
echo " Config: /etc/hive/"
echo " Data: /var/lib/hive/"
echo " Logs: /var/log/hive/"
echo ""
echo " Next steps:"
echo ""
echo " 1. Setup OVN networking:"
echo " sudo /usr/local/share/hive/setup-ovn.sh --management"
echo ""
echo " 2. Initialize a region:"
echo " sudo hive admin init --region <region> --az <az> --node node1 --nodes 1"
echo ""
echo " 3. Trust the CA certificate:"
echo " sudo cp /etc/hive/ca.pem /usr/local/share/ca-certificates/hive-ca.crt"
echo " sudo update-ca-certificates"
echo ""
echo " 4. Start services:"
echo " sudo systemctl start hive.target"
echo ""
echo " 5. Verify:"
echo " export AWS_PROFILE=hive"
echo " aws ec2 describe-instance-types"
echo ""
}
# --- Main ---
main() {
info "Hive installer"
echo ""
check_root
detect_os
detect_arch
handle_upgrade
install_apt_deps
install_aws_cli
detect_service_user
download_hive
install_files
create_directories
install_systemd
install_logrotate
rm -rf "$HIVE_TMPDIR"
restart_if_needed
print_summary
}
main "$@"