mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-03-17 05:13:01 +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")
|
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)
|
// Set up WireGuard sudoers (allows debros user to manage WG peers)
|
||||||
if err := ps.userProvisioner.SetupWireGuardSudoers(); err != nil {
|
if err := ps.userProvisioner.SetupWireGuardSudoers(); err != nil {
|
||||||
ps.logf(" ⚠️ Failed to setup wireguard sudoers: %v", err)
|
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
|
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
|
// SetupWireGuardSudoers configures the debros user with permissions to manage WireGuard
|
||||||
func (up *UserProvisioner) SetupWireGuardSudoers() error {
|
func (up *UserProvisioner) SetupWireGuardSudoers() error {
|
||||||
sudoersFile := "/etc/sudoers.d/debros-wireguard"
|
sudoersFile := "/etc/sudoers.d/debros-wireguard"
|
||||||
|
|||||||
@ -21,7 +21,6 @@ import (
|
|||||||
"github.com/DeBrosOfficial/network/pkg/systemd"
|
"github.com/DeBrosOfficial/network/pkg/systemd"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ClusterManagerConfig contains configuration for the cluster manager
|
// 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)
|
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 {
|
func (cm *ClusterManager) spawnOlricWithSystemd(ctx context.Context, cfg olric.InstanceConfig) error {
|
||||||
// Generate Olric config file (YAML)
|
// SystemdSpawner now handles config file creation
|
||||||
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
|
|
||||||
return cm.systemdSpawner.SpawnOlric(ctx, cfg.Namespace, cfg.NodeID, cfg)
|
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)
|
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 {
|
func (cm *ClusterManager) spawnGatewayWithSystemd(ctx context.Context, cfg gateway.InstanceConfig) error {
|
||||||
// Generate Gateway config file (YAML)
|
// SystemdSpawner now handles config file creation
|
||||||
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
|
|
||||||
return cm.systemdSpawner.SpawnGateway(ctx, cfg.Namespace, cfg.NodeID, cfg)
|
return cm.systemdSpawner.SpawnGateway(ctx, cfg.Namespace, cfg.NodeID, cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,8 @@ package namespace
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/DeBrosOfficial/network/pkg/gateway"
|
"github.com/DeBrosOfficial/network/pkg/gateway"
|
||||||
@ -10,6 +12,7 @@ import (
|
|||||||
"github.com/DeBrosOfficial/network/pkg/rqlite"
|
"github.com/DeBrosOfficial/network/pkg/rqlite"
|
||||||
"github.com/DeBrosOfficial/network/pkg/systemd"
|
"github.com/DeBrosOfficial/network/pkg/systemd"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SystemdSpawner spawns namespace cluster processes using systemd services
|
// 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("namespace", namespace),
|
||||||
zap.String("node_id", nodeID))
|
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{
|
envVars := map[string]string{
|
||||||
// No additional env vars needed - Olric reads from config file
|
"OLRIC_SERVER_CONFIG": configPath,
|
||||||
// The config file path is set in the systemd unit file
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.systemdMgr.GenerateEnvFile(namespace, nodeID, systemd.ServiceTypeOlric, envVars); err != nil {
|
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("namespace", namespace),
|
||||||
zap.String("node_id", nodeID))
|
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{
|
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 {
|
if err := s.systemdMgr.GenerateEnvFile(namespace, nodeID, systemd.ServiceTypeGateway, envVars); err != nil {
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
olriclib "github.com/olric-data/olric"
|
olriclib "github.com/olric-data/olric"
|
||||||
|
"github.com/olric-data/olric/config"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -33,14 +34,23 @@ func NewClient(cfg Config, logger *zap.Logger) (*Client, error) {
|
|||||||
servers = []string{"localhost:3320"}
|
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
|
timeout := cfg.Timeout
|
||||||
if timeout == 0 {
|
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{
|
return &Client{
|
||||||
|
|||||||
@ -47,12 +47,24 @@ func (m *Manager) StartService(namespace string, serviceType ServiceType) error
|
|||||||
zap.String("service", svcName),
|
zap.String("service", svcName),
|
||||||
zap.String("namespace", namespace))
|
zap.String("namespace", namespace))
|
||||||
|
|
||||||
cmd := exec.Command("systemctl", "start", svcName)
|
cmd := exec.Command("sudo", "-n", "systemctl", "start", svcName)
|
||||||
if output, err := cmd.CombinedOutput(); err != nil {
|
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))
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +75,7 @@ func (m *Manager) StopService(namespace string, serviceType ServiceType) error {
|
|||||||
zap.String("service", svcName),
|
zap.String("service", svcName),
|
||||||
zap.String("namespace", namespace))
|
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 {
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
// Don't error if service is already stopped or doesn't exist
|
// Don't error if service is already stopped or doesn't exist
|
||||||
if strings.Contains(string(output), "not loaded") || strings.Contains(string(output), "inactive") {
|
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("service", svcName),
|
||||||
zap.String("namespace", namespace))
|
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 {
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
return fmt.Errorf("failed to restart %s: %w\nOutput: %s", svcName, err, string(output))
|
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("service", svcName),
|
||||||
zap.String("namespace", namespace))
|
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 {
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
return fmt.Errorf("failed to enable %s: %w\nOutput: %s", svcName, err, string(output))
|
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("service", svcName),
|
||||||
zap.String("namespace", namespace))
|
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 {
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
// Don't error if service is already disabled or doesn't exist
|
// Don't error if service is already disabled or doesn't exist
|
||||||
if strings.Contains(string(output), "not loaded") {
|
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
|
// IsServiceActive checks if a namespace service is active
|
||||||
func (m *Manager) IsServiceActive(namespace string, serviceType ServiceType) (bool, error) {
|
func (m *Manager) IsServiceActive(namespace string, serviceType ServiceType) (bool, error) {
|
||||||
svcName := m.serviceName(namespace, serviceType)
|
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()
|
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 {
|
if err != nil {
|
||||||
// is-active returns exit code 3 if service is inactive
|
// is-active returns exit code 3 if service is inactive/activating
|
||||||
if strings.TrimSpace(string(output)) == "inactive" || strings.TrimSpace(string(output)) == "failed" {
|
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, 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
|
// ReloadDaemon reloads systemd daemon configuration
|
||||||
func (m *Manager) ReloadDaemon() error {
|
func (m *Manager) ReloadDaemon() error {
|
||||||
m.logger.Info("Reloading systemd daemon")
|
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 {
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
return fmt.Errorf("failed to reload systemd daemon: %w\nOutput: %s", err, string(output))
|
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
|
// ListNamespaceServices returns all namespace services currently registered in systemd
|
||||||
func (m *Manager) ListNamespaceServices() ([]string, error) {
|
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()
|
output, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to list namespace services: %w\nOutput: %s", err, string(output))
|
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 {
|
for _, svc := range services {
|
||||||
m.logger.Info("Stopping service", zap.String("service", svc))
|
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 {
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
m.logger.Warn("Failed to stop service",
|
m.logger.Warn("Failed to stop service",
|
||||||
zap.String("service", svc),
|
zap.String("service", svc),
|
||||||
@ -258,7 +293,13 @@ func (m *Manager) CleanupOrphanedProcesses() error {
|
|||||||
// GenerateEnvFile creates the environment file for a namespace service
|
// GenerateEnvFile creates the environment file for a namespace service
|
||||||
func (m *Manager) GenerateEnvFile(namespace, nodeID string, serviceType ServiceType, envVars map[string]string) error {
|
func (m *Manager) GenerateEnvFile(namespace, nodeID string, serviceType ServiceType, envVars map[string]string) error {
|
||||||
envDir := filepath.Join(m.namespaceBase, namespace)
|
envDir := filepath.Join(m.namespaceBase, namespace)
|
||||||
|
m.logger.Debug("Creating env directory",
|
||||||
|
zap.String("dir", envDir))
|
||||||
|
|
||||||
if err := os.MkdirAll(envDir, 0755); err != nil {
|
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)
|
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))
|
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 {
|
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)
|
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
|
for bin in orama-node gateway identity rqlite-mcp olric-server orama; do
|
||||||
if [ -f "$SRC_DIR/bin-linux/$bin" ]; then
|
if [ -f "$SRC_DIR/bin-linux/$bin" ]; then
|
||||||
cp "$SRC_DIR/bin-linux/$bin" "$BIN_DIR/$bin"
|
# Atomic rename: copy to temp, then move (works even if binary is running)
|
||||||
chmod +x "$BIN_DIR/$bin"
|
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"
|
echo " ✓ $bin → $BIN_DIR/$bin"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ -f "$SRC_DIR/bin-linux/coredns" ]; then
|
if [ -f "$SRC_DIR/bin-linux/coredns" ]; then
|
||||||
cp "$SRC_DIR/bin-linux/coredns" /usr/local/bin/coredns
|
cp "$SRC_DIR/bin-linux/coredns" /usr/local/bin/coredns.tmp
|
||||||
chmod +x /usr/local/bin/coredns
|
chmod +x /usr/local/bin/coredns.tmp
|
||||||
|
mv -f /usr/local/bin/coredns.tmp /usr/local/bin/coredns
|
||||||
echo " ✓ coredns → /usr/local/bin/coredns"
|
echo " ✓ coredns → /usr/local/bin/coredns"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -f "$SRC_DIR/bin-linux/caddy" ]; then
|
if [ -f "$SRC_DIR/bin-linux/caddy" ]; then
|
||||||
cp "$SRC_DIR/bin-linux/caddy" /usr/bin/caddy
|
cp "$SRC_DIR/bin-linux/caddy" /usr/bin/caddy.tmp
|
||||||
chmod +x /usr/bin/caddy
|
chmod +x /usr/bin/caddy.tmp
|
||||||
|
mv -f /usr/bin/caddy.tmp /usr/bin/caddy
|
||||||
echo " ✓ caddy → /usr/bin/caddy"
|
echo " ✓ caddy → /usr/bin/caddy"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,8 @@ WorkingDirectory=/home/debros
|
|||||||
|
|
||||||
EnvironmentFile=/home/debros/.orama/data/namespaces/%i/gateway.env
|
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
|
TimeoutStopSec=30s
|
||||||
KillMode=mixed
|
KillMode=mixed
|
||||||
|
|||||||
@ -11,11 +11,10 @@ User=debros
|
|||||||
Group=debros
|
Group=debros
|
||||||
WorkingDirectory=/home/debros
|
WorkingDirectory=/home/debros
|
||||||
|
|
||||||
# Olric reads config from environment variable
|
# Olric reads config from environment variable (set in env file)
|
||||||
Environment="OLRIC_SERVER_CONFIG=/home/debros/.orama/data/namespaces/%i/configs/olric-${NODE_ID}.yaml"
|
|
||||||
EnvironmentFile=/home/debros/.orama/data/namespaces/%i/olric.env
|
EnvironmentFile=/home/debros/.orama/data/namespaces/%i/olric.env
|
||||||
|
|
||||||
ExecStart=/usr/local/bin/olric-server
|
ExecStart=/home/debros/bin/olric-server
|
||||||
|
|
||||||
TimeoutStopSec=30s
|
TimeoutStopSec=30s
|
||||||
KillMode=mixed
|
KillMode=mixed
|
||||||
|
|||||||
@ -14,14 +14,14 @@ WorkingDirectory=/home/debros
|
|||||||
# Environment file contains namespace-specific config
|
# Environment file contains namespace-specific config
|
||||||
EnvironmentFile=/home/debros/.orama/data/namespaces/%i/rqlite.env
|
EnvironmentFile=/home/debros/.orama/data/namespaces/%i/rqlite.env
|
||||||
|
|
||||||
# Start rqlited with args from environment
|
# Start rqlited with args from environment (using shell to properly expand JOIN_ARGS)
|
||||||
ExecStart=/usr/local/bin/rqlited \
|
ExecStart=/bin/sh -c 'exec /usr/local/bin/rqlited \
|
||||||
-http-addr ${HTTP_ADDR} \
|
-http-addr ${HTTP_ADDR} \
|
||||||
-raft-addr ${RAFT_ADDR} \
|
-raft-addr ${RAFT_ADDR} \
|
||||||
-http-adv-addr ${HTTP_ADV_ADDR} \
|
-http-adv-addr ${HTTP_ADV_ADDR} \
|
||||||
-raft-adv-addr ${RAFT_ADV_ADDR} \
|
-raft-adv-addr ${RAFT_ADV_ADDR} \
|
||||||
${JOIN_ARGS} \
|
${JOIN_ARGS} \
|
||||||
/home/debros/.orama/data/namespaces/%i/rqlite/${NODE_ID}
|
/home/debros/.orama/data/namespaces/%i/rqlite/${NODE_ID}'
|
||||||
|
|
||||||
# Graceful shutdown
|
# Graceful shutdown
|
||||||
TimeoutStopSec=30s
|
TimeoutStopSec=30s
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user