mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 17:11:30 +00:00
new binary mode and systemd process
This commit is contained in:
@@ -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
|
||||
@@ -13,6 +13,7 @@ go.work.sum
|
||||
|
||||
# Build outputs
|
||||
bin/
|
||||
dist/
|
||||
tmp/
|
||||
*.test
|
||||
*.out
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/ /
|
||||
@@ -0,0 +1,9 @@
|
||||
/var/log/hive/*.log {
|
||||
daily
|
||||
rotate 14
|
||||
compress
|
||||
delaycompress
|
||||
missingok
|
||||
notifempty
|
||||
copytruncate
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Executable
+376
@@ -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 "$@"
|
||||
Reference in New Issue
Block a user