mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-03-17 03:13:00 +00:00
Fixed system service sudoer error on debros user
This commit is contained in:
parent
c855b790f8
commit
a7f100038d
@ -256,6 +256,13 @@ func (ps *ProductionSetup) Phase2ProvisionEnvironment() error {
|
||||
ps.logf(" ✓ Deployment sudoers configured")
|
||||
}
|
||||
|
||||
// Set up namespace sudoers (allows debros user to manage debros-namespace-* services)
|
||||
if err := ps.userProvisioner.SetupNamespaceSudoers(); err != nil {
|
||||
ps.logf(" ⚠️ Failed to setup namespace sudoers: %v", err)
|
||||
} else {
|
||||
ps.logf(" ✓ Namespace sudoers configured")
|
||||
}
|
||||
|
||||
// Set up WireGuard sudoers (allows debros user to manage WG peers)
|
||||
if err := ps.userProvisioner.SetupWireGuardSudoers(); err != nil {
|
||||
ps.logf(" ⚠️ Failed to setup wireguard sudoers: %v", err)
|
||||
|
||||
@ -224,6 +224,53 @@ debros ALL=(ALL) NOPASSWD: /bin/rm -f /etc/systemd/system/orama-deploy-*.service
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetupNamespaceSudoers configures the debros user with permissions needed for
|
||||
// managing namespace cluster services via systemd.
|
||||
func (up *UserProvisioner) SetupNamespaceSudoers() error {
|
||||
sudoersFile := "/etc/sudoers.d/debros-namespaces"
|
||||
|
||||
// Check if already configured
|
||||
if _, err := os.Stat(sudoersFile); err == nil {
|
||||
return nil // Already configured
|
||||
}
|
||||
|
||||
sudoersContent := `# DeBros Network - Namespace Cluster Management Permissions
|
||||
# Allows debros user to manage systemd services for namespace clusters
|
||||
|
||||
# Systemd service management for debros-namespace-* services
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl daemon-reload
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl start debros-namespace-*
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl stop debros-namespace-*
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart debros-namespace-*
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl enable debros-namespace-*
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl disable debros-namespace-*
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl status debros-namespace-*
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/systemctl is-active debros-namespace-*
|
||||
|
||||
# Service file management (tee to write, rm to remove)
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/tee /etc/systemd/system/debros-namespace-*.service
|
||||
debros ALL=(ALL) NOPASSWD: /bin/rm -f /etc/systemd/system/debros-namespace-*.service
|
||||
|
||||
# Environment file management for namespace services
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/tee /home/debros/.orama/namespace/*/env/*
|
||||
debros ALL=(ALL) NOPASSWD: /usr/bin/mkdir -p /home/debros/.orama/namespace/*/env
|
||||
`
|
||||
|
||||
// Write sudoers rule
|
||||
if err := os.WriteFile(sudoersFile, []byte(sudoersContent), 0440); err != nil {
|
||||
return fmt.Errorf("failed to create namespace sudoers rule: %w", err)
|
||||
}
|
||||
|
||||
// Validate sudoers file
|
||||
cmd := exec.Command("visudo", "-c", "-f", sudoersFile)
|
||||
if err := cmd.Run(); err != nil {
|
||||
os.Remove(sudoersFile) // Clean up on validation failure
|
||||
return fmt.Errorf("namespace sudoers rule validation failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetupWireGuardSudoers configures the debros user with permissions to manage WireGuard
|
||||
func (up *UserProvisioner) SetupWireGuardSudoers() error {
|
||||
sudoersFile := "/etc/sudoers.d/debros-wireguard"
|
||||
|
||||
@ -21,7 +21,6 @@ import (
|
||||
"github.com/DeBrosOfficial/network/pkg/systemd"
|
||||
"github.com/google/uuid"
|
||||
"go.uber.org/zap"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// ClusterManagerConfig contains configuration for the cluster manager
|
||||
@ -164,57 +163,9 @@ func (cm *ClusterManager) spawnRQLiteWithSystemd(ctx context.Context, cfg rqlite
|
||||
return cm.systemdSpawner.SpawnRQLite(ctx, cfg.Namespace, cfg.NodeID, cfg)
|
||||
}
|
||||
|
||||
// spawnOlricWithSystemd generates config and spawns Olric via systemd
|
||||
// spawnOlricWithSystemd spawns Olric via systemd (config creation now handled by spawner)
|
||||
func (cm *ClusterManager) spawnOlricWithSystemd(ctx context.Context, cfg olric.InstanceConfig) error {
|
||||
// Generate Olric config file (YAML)
|
||||
configDir := filepath.Join(cm.baseDataDir, cfg.Namespace, "configs")
|
||||
if err := os.MkdirAll(configDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create config directory: %w", err)
|
||||
}
|
||||
|
||||
configPath := filepath.Join(configDir, fmt.Sprintf("olric-%s.yaml", cfg.NodeID))
|
||||
|
||||
// Generate Olric YAML config
|
||||
type olricServerConfig struct {
|
||||
BindAddr string `yaml:"bindAddr"`
|
||||
BindPort int `yaml:"bindPort"`
|
||||
}
|
||||
type olricMemberlistConfig struct {
|
||||
Environment string `yaml:"environment"`
|
||||
BindAddr string `yaml:"bindAddr"`
|
||||
BindPort int `yaml:"bindPort"`
|
||||
Peers []string `yaml:"peers,omitempty"`
|
||||
}
|
||||
type olricConfig struct {
|
||||
Server olricServerConfig `yaml:"server"`
|
||||
Memberlist olricMemberlistConfig `yaml:"memberlist"`
|
||||
PartitionCount uint64 `yaml:"partitionCount"`
|
||||
}
|
||||
|
||||
config := olricConfig{
|
||||
Server: olricServerConfig{
|
||||
BindAddr: cfg.BindAddr,
|
||||
BindPort: cfg.HTTPPort,
|
||||
},
|
||||
Memberlist: olricMemberlistConfig{
|
||||
Environment: "lan",
|
||||
BindAddr: cfg.BindAddr,
|
||||
BindPort: cfg.MemberlistPort,
|
||||
Peers: cfg.PeerAddresses,
|
||||
},
|
||||
PartitionCount: 12, // Optimized for namespace clusters (vs 256 default)
|
||||
}
|
||||
|
||||
configBytes, err := yaml.Marshal(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal Olric config: %w", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(configPath, configBytes, 0644); err != nil {
|
||||
return fmt.Errorf("failed to write Olric config: %w", err)
|
||||
}
|
||||
|
||||
// Start via systemd
|
||||
// SystemdSpawner now handles config file creation
|
||||
return cm.systemdSpawner.SpawnOlric(ctx, cfg.Namespace, cfg.NodeID, cfg)
|
||||
}
|
||||
|
||||
@ -234,55 +185,9 @@ func (cm *ClusterManager) writePeersJSON(dataDir string, peers []rqlite.RaftPeer
|
||||
return os.WriteFile(peersFile, data, 0644)
|
||||
}
|
||||
|
||||
// spawnGatewayWithSystemd generates config and spawns Gateway via systemd
|
||||
// spawnGatewayWithSystemd spawns Gateway via systemd (config creation now handled by spawner)
|
||||
func (cm *ClusterManager) spawnGatewayWithSystemd(ctx context.Context, cfg gateway.InstanceConfig) error {
|
||||
// Generate Gateway config file (YAML)
|
||||
configDir := filepath.Join(cm.baseDataDir, cfg.Namespace, "configs")
|
||||
if err := os.MkdirAll(configDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create config directory: %w", err)
|
||||
}
|
||||
|
||||
configPath := filepath.Join(configDir, fmt.Sprintf("gateway-%s.yaml", cfg.NodeID))
|
||||
|
||||
// Build Gateway YAML config
|
||||
type gatewayYAMLConfig struct {
|
||||
ListenAddr string `yaml:"listen_addr"`
|
||||
ClientNamespace string `yaml:"client_namespace"`
|
||||
RQLiteDSN string `yaml:"rqlite_dsn"`
|
||||
GlobalRQLiteDSN string `yaml:"global_rqlite_dsn,omitempty"`
|
||||
DomainName string `yaml:"domain_name"`
|
||||
OlricServers []string `yaml:"olric_servers"`
|
||||
OlricTimeout string `yaml:"olric_timeout"`
|
||||
IPFSClusterAPIURL string `yaml:"ipfs_cluster_api_url"`
|
||||
IPFSAPIURL string `yaml:"ipfs_api_url"`
|
||||
IPFSTimeout string `yaml:"ipfs_timeout"`
|
||||
IPFSReplicationFactor int `yaml:"ipfs_replication_factor"`
|
||||
}
|
||||
|
||||
gatewayConfig := gatewayYAMLConfig{
|
||||
ListenAddr: fmt.Sprintf(":%d", cfg.HTTPPort),
|
||||
ClientNamespace: cfg.Namespace,
|
||||
RQLiteDSN: cfg.RQLiteDSN,
|
||||
GlobalRQLiteDSN: cfg.GlobalRQLiteDSN,
|
||||
DomainName: cfg.BaseDomain,
|
||||
OlricServers: cfg.OlricServers,
|
||||
OlricTimeout: cfg.OlricTimeout.String(),
|
||||
IPFSClusterAPIURL: cfg.IPFSClusterAPIURL,
|
||||
IPFSAPIURL: cfg.IPFSAPIURL,
|
||||
IPFSTimeout: cfg.IPFSTimeout.String(),
|
||||
IPFSReplicationFactor: cfg.IPFSReplicationFactor,
|
||||
}
|
||||
|
||||
configBytes, err := yaml.Marshal(gatewayConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal Gateway config: %w", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(configPath, configBytes, 0644); err != nil {
|
||||
return fmt.Errorf("failed to write Gateway config: %w", err)
|
||||
}
|
||||
|
||||
// Start via systemd
|
||||
// SystemdSpawner now handles config file creation
|
||||
return cm.systemdSpawner.SpawnGateway(ctx, cfg.Namespace, cfg.NodeID, cfg)
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,8 @@ package namespace
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/DeBrosOfficial/network/pkg/gateway"
|
||||
@ -10,6 +12,7 @@ import (
|
||||
"github.com/DeBrosOfficial/network/pkg/rqlite"
|
||||
"github.com/DeBrosOfficial/network/pkg/systemd"
|
||||
"go.uber.org/zap"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// SystemdSpawner spawns namespace cluster processes using systemd services
|
||||
@ -80,10 +83,62 @@ func (s *SystemdSpawner) SpawnOlric(ctx context.Context, namespace, nodeID strin
|
||||
zap.String("namespace", namespace),
|
||||
zap.String("node_id", nodeID))
|
||||
|
||||
// Generate environment file (Olric uses OLRIC_SERVER_CONFIG env var, set in systemd unit)
|
||||
// Create config directory
|
||||
configDir := filepath.Join(s.namespaceBase, namespace, "configs")
|
||||
if err := os.MkdirAll(configDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create config directory: %w", err)
|
||||
}
|
||||
|
||||
configPath := filepath.Join(configDir, fmt.Sprintf("olric-%s.yaml", nodeID))
|
||||
|
||||
// Generate Olric YAML config
|
||||
type olricServerConfig struct {
|
||||
BindAddr string `yaml:"bindAddr"`
|
||||
BindPort int `yaml:"bindPort"`
|
||||
}
|
||||
type olricMemberlistConfig struct {
|
||||
Environment string `yaml:"environment"`
|
||||
BindAddr string `yaml:"bindAddr"`
|
||||
BindPort int `yaml:"bindPort"`
|
||||
Peers []string `yaml:"peers,omitempty"`
|
||||
}
|
||||
type olricConfig struct {
|
||||
Server olricServerConfig `yaml:"server"`
|
||||
Memberlist olricMemberlistConfig `yaml:"memberlist"`
|
||||
PartitionCount uint64 `yaml:"partitionCount"`
|
||||
}
|
||||
|
||||
config := olricConfig{
|
||||
Server: olricServerConfig{
|
||||
BindAddr: cfg.BindAddr,
|
||||
BindPort: cfg.HTTPPort,
|
||||
},
|
||||
Memberlist: olricMemberlistConfig{
|
||||
Environment: "lan",
|
||||
BindAddr: cfg.BindAddr,
|
||||
BindPort: cfg.MemberlistPort,
|
||||
Peers: cfg.PeerAddresses,
|
||||
},
|
||||
PartitionCount: 12, // Optimized for namespace clusters (vs 256 default)
|
||||
}
|
||||
|
||||
configBytes, err := yaml.Marshal(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal Olric config: %w", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(configPath, configBytes, 0644); err != nil {
|
||||
return fmt.Errorf("failed to write Olric config: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("Created Olric config file",
|
||||
zap.String("path", configPath),
|
||||
zap.String("namespace", namespace),
|
||||
zap.String("node_id", nodeID))
|
||||
|
||||
// Generate environment file with Olric config path
|
||||
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
|
||||
"OLRIC_SERVER_CONFIG": configPath,
|
||||
}
|
||||
|
||||
if err := s.systemdMgr.GenerateEnvFile(namespace, nodeID, systemd.ServiceTypeOlric, envVars); err != nil {
|
||||
@ -113,9 +168,60 @@ func (s *SystemdSpawner) SpawnGateway(ctx context.Context, namespace, nodeID str
|
||||
zap.String("namespace", namespace),
|
||||
zap.String("node_id", nodeID))
|
||||
|
||||
// Generate environment file
|
||||
// Create config directory
|
||||
configDir := filepath.Join(s.namespaceBase, namespace, "configs")
|
||||
if err := os.MkdirAll(configDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create config directory: %w", err)
|
||||
}
|
||||
|
||||
configPath := filepath.Join(configDir, fmt.Sprintf("gateway-%s.yaml", nodeID))
|
||||
|
||||
// Build Gateway YAML config
|
||||
type gatewayYAMLConfig struct {
|
||||
ListenAddr string `yaml:"listen_addr"`
|
||||
ClientNamespace string `yaml:"client_namespace"`
|
||||
RQLiteDSN string `yaml:"rqlite_dsn"`
|
||||
GlobalRQLiteDSN string `yaml:"global_rqlite_dsn,omitempty"`
|
||||
DomainName string `yaml:"domain_name"`
|
||||
OlricServers []string `yaml:"olric_servers"`
|
||||
OlricTimeout string `yaml:"olric_timeout"`
|
||||
IPFSClusterAPIURL string `yaml:"ipfs_cluster_api_url"`
|
||||
IPFSAPIURL string `yaml:"ipfs_api_url"`
|
||||
IPFSTimeout string `yaml:"ipfs_timeout"`
|
||||
IPFSReplicationFactor int `yaml:"ipfs_replication_factor"`
|
||||
}
|
||||
|
||||
gatewayConfig := gatewayYAMLConfig{
|
||||
ListenAddr: fmt.Sprintf(":%d", cfg.HTTPPort),
|
||||
ClientNamespace: cfg.Namespace,
|
||||
RQLiteDSN: cfg.RQLiteDSN,
|
||||
GlobalRQLiteDSN: cfg.GlobalRQLiteDSN,
|
||||
DomainName: cfg.BaseDomain,
|
||||
OlricServers: cfg.OlricServers,
|
||||
OlricTimeout: cfg.OlricTimeout.String(),
|
||||
IPFSClusterAPIURL: cfg.IPFSClusterAPIURL,
|
||||
IPFSAPIURL: cfg.IPFSAPIURL,
|
||||
IPFSTimeout: cfg.IPFSTimeout.String(),
|
||||
IPFSReplicationFactor: cfg.IPFSReplicationFactor,
|
||||
}
|
||||
|
||||
configBytes, err := yaml.Marshal(gatewayConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal Gateway config: %w", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(configPath, configBytes, 0644); err != nil {
|
||||
return fmt.Errorf("failed to write Gateway config: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("Created Gateway config file",
|
||||
zap.String("path", configPath),
|
||||
zap.String("namespace", namespace),
|
||||
zap.String("node_id", nodeID))
|
||||
|
||||
// Generate environment file with Gateway config path
|
||||
envVars := map[string]string{
|
||||
// Gateway uses config file, no additional env vars needed
|
||||
"GATEWAY_CONFIG": configPath,
|
||||
}
|
||||
|
||||
if err := s.systemdMgr.GenerateEnvFile(namespace, nodeID, systemd.ServiceTypeGateway, envVars); err != nil {
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
olriclib "github.com/olric-data/olric"
|
||||
"github.com/olric-data/olric/config"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@ -33,14 +34,23 @@ func NewClient(cfg Config, logger *zap.Logger) (*Client, error) {
|
||||
servers = []string{"localhost:3320"}
|
||||
}
|
||||
|
||||
client, err := olriclib.NewClusterClient(servers)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create Olric cluster client: %w", err)
|
||||
}
|
||||
|
||||
timeout := cfg.Timeout
|
||||
if timeout == 0 {
|
||||
timeout = 10 * time.Second
|
||||
timeout = 30 * time.Second // Increased default timeout for slow SCAN operations
|
||||
}
|
||||
|
||||
// Configure client with increased timeouts for slow operations
|
||||
clientCfg := &config.Client{
|
||||
DialTimeout: 5 * time.Second,
|
||||
ReadTimeout: timeout, // 30s default - enough for slow SCAN operations
|
||||
WriteTimeout: timeout,
|
||||
MaxRetries: 1, // Reduce retries to 1 to avoid excessive delays
|
||||
Authentication: &config.Authentication{}, // Initialize to prevent nil pointer
|
||||
}
|
||||
|
||||
client, err := olriclib.NewClusterClient(servers, olriclib.WithConfig(clientCfg))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create Olric cluster client: %w", err)
|
||||
}
|
||||
|
||||
return &Client{
|
||||
|
||||
@ -47,12 +47,24 @@ func (m *Manager) StartService(namespace string, serviceType ServiceType) error
|
||||
zap.String("service", svcName),
|
||||
zap.String("namespace", namespace))
|
||||
|
||||
cmd := exec.Command("systemctl", "start", svcName)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
cmd := exec.Command("sudo", "-n", "systemctl", "start", svcName)
|
||||
m.logger.Debug("Executing systemctl command",
|
||||
zap.String("cmd", cmd.String()),
|
||||
zap.Strings("args", cmd.Args))
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
m.logger.Error("Failed to start service",
|
||||
zap.String("service", svcName),
|
||||
zap.Error(err),
|
||||
zap.String("output", string(output)),
|
||||
zap.String("cmd", cmd.String()))
|
||||
return fmt.Errorf("failed to start %s: %w\nOutput: %s", svcName, err, string(output))
|
||||
}
|
||||
|
||||
m.logger.Info("Service started successfully", zap.String("service", svcName))
|
||||
m.logger.Info("Service started successfully",
|
||||
zap.String("service", svcName),
|
||||
zap.String("output", string(output)))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -63,7 +75,7 @@ func (m *Manager) StopService(namespace string, serviceType ServiceType) error {
|
||||
zap.String("service", svcName),
|
||||
zap.String("namespace", namespace))
|
||||
|
||||
cmd := exec.Command("systemctl", "stop", svcName)
|
||||
cmd := exec.Command("sudo", "-n", "systemctl", "stop", svcName)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
// Don't error if service is already stopped or doesn't exist
|
||||
if strings.Contains(string(output), "not loaded") || strings.Contains(string(output), "inactive") {
|
||||
@ -84,7 +96,7 @@ func (m *Manager) RestartService(namespace string, serviceType ServiceType) erro
|
||||
zap.String("service", svcName),
|
||||
zap.String("namespace", namespace))
|
||||
|
||||
cmd := exec.Command("systemctl", "restart", svcName)
|
||||
cmd := exec.Command("sudo", "-n", "systemctl", "restart", svcName)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to restart %s: %w\nOutput: %s", svcName, err, string(output))
|
||||
}
|
||||
@ -100,7 +112,7 @@ func (m *Manager) EnableService(namespace string, serviceType ServiceType) error
|
||||
zap.String("service", svcName),
|
||||
zap.String("namespace", namespace))
|
||||
|
||||
cmd := exec.Command("systemctl", "enable", svcName)
|
||||
cmd := exec.Command("sudo", "-n", "systemctl", "enable", svcName)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to enable %s: %w\nOutput: %s", svcName, err, string(output))
|
||||
}
|
||||
@ -116,7 +128,7 @@ func (m *Manager) DisableService(namespace string, serviceType ServiceType) erro
|
||||
zap.String("service", svcName),
|
||||
zap.String("namespace", namespace))
|
||||
|
||||
cmd := exec.Command("systemctl", "disable", svcName)
|
||||
cmd := exec.Command("sudo", "-n", "systemctl", "disable", svcName)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
// Don't error if service is already disabled or doesn't exist
|
||||
if strings.Contains(string(output), "not loaded") {
|
||||
@ -133,24 +145,47 @@ func (m *Manager) DisableService(namespace string, serviceType ServiceType) erro
|
||||
// IsServiceActive checks if a namespace service is active
|
||||
func (m *Manager) IsServiceActive(namespace string, serviceType ServiceType) (bool, error) {
|
||||
svcName := m.serviceName(namespace, serviceType)
|
||||
cmd := exec.Command("systemctl", "is-active", svcName)
|
||||
cmd := exec.Command("sudo", "-n", "systemctl", "is-active", svcName)
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
outputStr := strings.TrimSpace(string(output))
|
||||
m.logger.Debug("Checking service status",
|
||||
zap.String("service", svcName),
|
||||
zap.String("status", outputStr),
|
||||
zap.Error(err))
|
||||
|
||||
if err != nil {
|
||||
// is-active returns exit code 3 if service is inactive
|
||||
if strings.TrimSpace(string(output)) == "inactive" || strings.TrimSpace(string(output)) == "failed" {
|
||||
// is-active returns exit code 3 if service is inactive/activating
|
||||
if outputStr == "inactive" || outputStr == "failed" {
|
||||
m.logger.Debug("Service is not active",
|
||||
zap.String("service", svcName),
|
||||
zap.String("status", outputStr))
|
||||
return false, nil
|
||||
}
|
||||
return false, fmt.Errorf("failed to check service status: %w\nOutput: %s", err, string(output))
|
||||
// "activating" means the service is starting - return false to wait longer, but no error
|
||||
if outputStr == "activating" {
|
||||
m.logger.Debug("Service is still activating",
|
||||
zap.String("service", svcName))
|
||||
return false, nil
|
||||
}
|
||||
m.logger.Error("Failed to check service status",
|
||||
zap.String("service", svcName),
|
||||
zap.Error(err),
|
||||
zap.String("output", outputStr))
|
||||
return false, fmt.Errorf("failed to check service status: %w\nOutput: %s", err, outputStr)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(output)) == "active", nil
|
||||
isActive := outputStr == "active"
|
||||
m.logger.Debug("Service status check complete",
|
||||
zap.String("service", svcName),
|
||||
zap.Bool("active", isActive))
|
||||
return isActive, nil
|
||||
}
|
||||
|
||||
// ReloadDaemon reloads systemd daemon configuration
|
||||
func (m *Manager) ReloadDaemon() error {
|
||||
m.logger.Info("Reloading systemd daemon")
|
||||
cmd := exec.Command("systemctl", "daemon-reload")
|
||||
cmd := exec.Command("sudo", "-n", "systemctl", "daemon-reload")
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to reload systemd daemon: %w\nOutput: %s", err, string(output))
|
||||
}
|
||||
@ -193,7 +228,7 @@ func (m *Manager) StartAllNamespaceServices(namespace string) error {
|
||||
|
||||
// ListNamespaceServices returns all namespace services currently registered in systemd
|
||||
func (m *Manager) ListNamespaceServices() ([]string, error) {
|
||||
cmd := exec.Command("systemctl", "list-units", "--all", "--no-legend", "debros-namespace-*@*.service")
|
||||
cmd := exec.Command("sudo", "-n", "systemctl", "list-units", "--all", "--no-legend", "debros-namespace-*@*.service")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list namespace services: %w\nOutput: %s", err, string(output))
|
||||
@ -225,7 +260,7 @@ func (m *Manager) StopAllNamespaceServicesGlobally() error {
|
||||
|
||||
for _, svc := range services {
|
||||
m.logger.Info("Stopping service", zap.String("service", svc))
|
||||
cmd := exec.Command("systemctl", "stop", svc)
|
||||
cmd := exec.Command("sudo", "-n", "systemctl", "stop", svc)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
m.logger.Warn("Failed to stop service",
|
||||
zap.String("service", svc),
|
||||
@ -258,7 +293,13 @@ func (m *Manager) CleanupOrphanedProcesses() error {
|
||||
// GenerateEnvFile creates the environment file for a namespace service
|
||||
func (m *Manager) GenerateEnvFile(namespace, nodeID string, serviceType ServiceType, envVars map[string]string) error {
|
||||
envDir := filepath.Join(m.namespaceBase, namespace)
|
||||
m.logger.Debug("Creating env directory",
|
||||
zap.String("dir", envDir))
|
||||
|
||||
if err := os.MkdirAll(envDir, 0755); err != nil {
|
||||
m.logger.Error("Failed to create env directory",
|
||||
zap.String("dir", envDir),
|
||||
zap.Error(err))
|
||||
return fmt.Errorf("failed to create env directory: %w", err)
|
||||
}
|
||||
|
||||
@ -278,7 +319,14 @@ func (m *Manager) GenerateEnvFile(namespace, nodeID string, serviceType ServiceT
|
||||
content.WriteString(fmt.Sprintf("%s=%s\n", key, value))
|
||||
}
|
||||
|
||||
m.logger.Debug("Writing env file",
|
||||
zap.String("file", envFile),
|
||||
zap.Int("size", content.Len()))
|
||||
|
||||
if err := os.WriteFile(envFile, []byte(content.String()), 0644); err != nil {
|
||||
m.logger.Error("Failed to write env file",
|
||||
zap.String("file", envFile),
|
||||
zap.Error(err))
|
||||
return fmt.Errorf("failed to write env file: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@ -49,21 +49,25 @@ if [ -d "$SRC_DIR/bin-linux" ]; then
|
||||
|
||||
for bin in orama-node gateway identity rqlite-mcp olric-server orama; do
|
||||
if [ -f "$SRC_DIR/bin-linux/$bin" ]; then
|
||||
cp "$SRC_DIR/bin-linux/$bin" "$BIN_DIR/$bin"
|
||||
chmod +x "$BIN_DIR/$bin"
|
||||
# Atomic rename: copy to temp, then move (works even if binary is running)
|
||||
cp "$SRC_DIR/bin-linux/$bin" "$BIN_DIR/$bin.tmp"
|
||||
chmod +x "$BIN_DIR/$bin.tmp"
|
||||
mv -f "$BIN_DIR/$bin.tmp" "$BIN_DIR/$bin"
|
||||
echo " ✓ $bin → $BIN_DIR/$bin"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -f "$SRC_DIR/bin-linux/coredns" ]; then
|
||||
cp "$SRC_DIR/bin-linux/coredns" /usr/local/bin/coredns
|
||||
chmod +x /usr/local/bin/coredns
|
||||
cp "$SRC_DIR/bin-linux/coredns" /usr/local/bin/coredns.tmp
|
||||
chmod +x /usr/local/bin/coredns.tmp
|
||||
mv -f /usr/local/bin/coredns.tmp /usr/local/bin/coredns
|
||||
echo " ✓ coredns → /usr/local/bin/coredns"
|
||||
fi
|
||||
|
||||
if [ -f "$SRC_DIR/bin-linux/caddy" ]; then
|
||||
cp "$SRC_DIR/bin-linux/caddy" /usr/bin/caddy
|
||||
chmod +x /usr/bin/caddy
|
||||
cp "$SRC_DIR/bin-linux/caddy" /usr/bin/caddy.tmp
|
||||
chmod +x /usr/bin/caddy.tmp
|
||||
mv -f /usr/bin/caddy.tmp /usr/bin/caddy
|
||||
echo " ✓ caddy → /usr/bin/caddy"
|
||||
fi
|
||||
|
||||
|
||||
@ -13,7 +13,8 @@ WorkingDirectory=/home/debros
|
||||
|
||||
EnvironmentFile=/home/debros/.orama/data/namespaces/%i/gateway.env
|
||||
|
||||
ExecStart=/home/debros/bin/gateway --config /home/debros/.orama/data/namespaces/%i/configs/gateway-${NODE_ID}.yaml
|
||||
# Use shell to properly expand NODE_ID from env file
|
||||
ExecStart=/bin/sh -c 'exec /home/debros/bin/gateway --config ${GATEWAY_CONFIG}'
|
||||
|
||||
TimeoutStopSec=30s
|
||||
KillMode=mixed
|
||||
|
||||
@ -11,11 +11,10 @@ User=debros
|
||||
Group=debros
|
||||
WorkingDirectory=/home/debros
|
||||
|
||||
# Olric reads config from environment variable
|
||||
Environment="OLRIC_SERVER_CONFIG=/home/debros/.orama/data/namespaces/%i/configs/olric-${NODE_ID}.yaml"
|
||||
# Olric reads config from environment variable (set in env file)
|
||||
EnvironmentFile=/home/debros/.orama/data/namespaces/%i/olric.env
|
||||
|
||||
ExecStart=/usr/local/bin/olric-server
|
||||
ExecStart=/home/debros/bin/olric-server
|
||||
|
||||
TimeoutStopSec=30s
|
||||
KillMode=mixed
|
||||
|
||||
@ -14,14 +14,14 @@ WorkingDirectory=/home/debros
|
||||
# Environment file contains namespace-specific config
|
||||
EnvironmentFile=/home/debros/.orama/data/namespaces/%i/rqlite.env
|
||||
|
||||
# Start rqlited with args from environment
|
||||
ExecStart=/usr/local/bin/rqlited \
|
||||
# Start rqlited with args from environment (using shell to properly expand JOIN_ARGS)
|
||||
ExecStart=/bin/sh -c 'exec /usr/local/bin/rqlited \
|
||||
-http-addr ${HTTP_ADDR} \
|
||||
-raft-addr ${RAFT_ADDR} \
|
||||
-http-adv-addr ${HTTP_ADV_ADDR} \
|
||||
-raft-adv-addr ${RAFT_ADV_ADDR} \
|
||||
${JOIN_ARGS} \
|
||||
/home/debros/.orama/data/namespaces/%i/rqlite/${NODE_ID}
|
||||
/home/debros/.orama/data/namespaces/%i/rqlite/${NODE_ID}'
|
||||
|
||||
# Graceful shutdown
|
||||
TimeoutStopSec=30s
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user