orama/pkg/namespace/systemd_spawner.go

196 lines
6.4 KiB
Go

package namespace
import (
"context"
"fmt"
"time"
"github.com/DeBrosOfficial/network/pkg/gateway"
"github.com/DeBrosOfficial/network/pkg/olric"
"github.com/DeBrosOfficial/network/pkg/rqlite"
"github.com/DeBrosOfficial/network/pkg/systemd"
"go.uber.org/zap"
)
// SystemdSpawner spawns namespace cluster processes using systemd services
type SystemdSpawner struct {
systemdMgr *systemd.Manager
namespaceBase string
logger *zap.Logger
}
// NewSystemdSpawner creates a new systemd-based spawner
func NewSystemdSpawner(namespaceBase string, logger *zap.Logger) *SystemdSpawner {
return &SystemdSpawner{
systemdMgr: systemd.NewManager(namespaceBase, logger),
namespaceBase: namespaceBase,
logger: logger.With(zap.String("component", "systemd-spawner")),
}
}
// SpawnRQLite starts a RQLite instance using systemd
func (s *SystemdSpawner) SpawnRQLite(ctx context.Context, namespace, nodeID string, cfg rqlite.InstanceConfig) error {
s.logger.Info("Spawning RQLite via systemd",
zap.String("namespace", namespace),
zap.String("node_id", nodeID))
// Build join arguments
joinArgs := ""
if len(cfg.JoinAddresses) > 0 {
joinArgs = fmt.Sprintf("-join %s", cfg.JoinAddresses[0])
for _, addr := range cfg.JoinAddresses[1:] {
joinArgs += fmt.Sprintf(",%s", addr)
}
}
// Generate environment file
envVars := map[string]string{
"HTTP_ADDR": fmt.Sprintf("0.0.0.0:%d", cfg.HTTPPort),
"RAFT_ADDR": fmt.Sprintf("0.0.0.0:%d", cfg.RaftPort),
"HTTP_ADV_ADDR": cfg.HTTPAdvAddress,
"RAFT_ADV_ADDR": cfg.RaftAdvAddress,
"JOIN_ARGS": joinArgs,
"NODE_ID": nodeID,
}
if err := s.systemdMgr.GenerateEnvFile(namespace, nodeID, systemd.ServiceTypeRQLite, envVars); err != nil {
return fmt.Errorf("failed to generate RQLite env file: %w", err)
}
// Start the systemd service
if err := s.systemdMgr.StartService(namespace, systemd.ServiceTypeRQLite); err != nil {
return fmt.Errorf("failed to start RQLite service: %w", err)
}
// Wait for service to be active
if err := s.waitForService(namespace, systemd.ServiceTypeRQLite, 30*time.Second); err != nil {
return fmt.Errorf("RQLite service did not become active: %w", err)
}
s.logger.Info("RQLite spawned successfully via systemd",
zap.String("namespace", namespace),
zap.String("node_id", nodeID))
return nil
}
// SpawnOlric starts an Olric instance using systemd
func (s *SystemdSpawner) SpawnOlric(ctx context.Context, namespace, nodeID string, cfg olric.InstanceConfig) error {
s.logger.Info("Spawning Olric via systemd",
zap.String("namespace", namespace),
zap.String("node_id", nodeID))
// Generate environment file (Olric uses OLRIC_SERVER_CONFIG env var, set in systemd unit)
envVars := map[string]string{
// No additional env vars needed - Olric reads from config file
// The config file path is set in the systemd unit file
}
if err := s.systemdMgr.GenerateEnvFile(namespace, nodeID, systemd.ServiceTypeOlric, envVars); err != nil {
return fmt.Errorf("failed to generate Olric env file: %w", err)
}
// Start the systemd service
if err := s.systemdMgr.StartService(namespace, systemd.ServiceTypeOlric); err != nil {
return fmt.Errorf("failed to start Olric service: %w", err)
}
// Wait for service to be active
if err := s.waitForService(namespace, systemd.ServiceTypeOlric, 30*time.Second); err != nil {
return fmt.Errorf("Olric service did not become active: %w", err)
}
s.logger.Info("Olric spawned successfully via systemd",
zap.String("namespace", namespace),
zap.String("node_id", nodeID))
return nil
}
// SpawnGateway starts a Gateway instance using systemd
func (s *SystemdSpawner) SpawnGateway(ctx context.Context, namespace, nodeID string, cfg gateway.InstanceConfig) error {
s.logger.Info("Spawning Gateway via systemd",
zap.String("namespace", namespace),
zap.String("node_id", nodeID))
// Generate environment file
envVars := map[string]string{
// Gateway uses config file, no additional env vars needed
}
if err := s.systemdMgr.GenerateEnvFile(namespace, nodeID, systemd.ServiceTypeGateway, envVars); err != nil {
return fmt.Errorf("failed to generate Gateway env file: %w", err)
}
// Start the systemd service
if err := s.systemdMgr.StartService(namespace, systemd.ServiceTypeGateway); err != nil {
return fmt.Errorf("failed to start Gateway service: %w", err)
}
// Wait for service to be active
if err := s.waitForService(namespace, systemd.ServiceTypeGateway, 30*time.Second); err != nil {
return fmt.Errorf("Gateway service did not become active: %w", err)
}
s.logger.Info("Gateway spawned successfully via systemd",
zap.String("namespace", namespace),
zap.String("node_id", nodeID))
return nil
}
// StopRQLite stops a RQLite instance
func (s *SystemdSpawner) StopRQLite(ctx context.Context, namespace, nodeID string) error {
s.logger.Info("Stopping RQLite via systemd",
zap.String("namespace", namespace),
zap.String("node_id", nodeID))
return s.systemdMgr.StopService(namespace, systemd.ServiceTypeRQLite)
}
// StopOlric stops an Olric instance
func (s *SystemdSpawner) StopOlric(ctx context.Context, namespace, nodeID string) error {
s.logger.Info("Stopping Olric via systemd",
zap.String("namespace", namespace),
zap.String("node_id", nodeID))
return s.systemdMgr.StopService(namespace, systemd.ServiceTypeOlric)
}
// StopGateway stops a Gateway instance
func (s *SystemdSpawner) StopGateway(ctx context.Context, namespace, nodeID string) error {
s.logger.Info("Stopping Gateway via systemd",
zap.String("namespace", namespace),
zap.String("node_id", nodeID))
return s.systemdMgr.StopService(namespace, systemd.ServiceTypeGateway)
}
// StopAll stops all services for a namespace
func (s *SystemdSpawner) StopAll(ctx context.Context, namespace string) error {
s.logger.Info("Stopping all namespace services via systemd",
zap.String("namespace", namespace))
return s.systemdMgr.StopAllNamespaceServices(namespace)
}
// waitForService waits for a systemd service to become active
func (s *SystemdSpawner) waitForService(namespace string, serviceType systemd.ServiceType, timeout time.Duration) error {
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
active, err := s.systemdMgr.IsServiceActive(namespace, serviceType)
if err != nil {
return fmt.Errorf("failed to check service status: %w", err)
}
if active {
return nil
}
time.Sleep(1 * time.Second)
}
return fmt.Errorf("service did not become active within %v", timeout)
}