mirror of
https://github.com/DeBrosOfficial/network.git
synced 2025-12-11 08:18:49 +00:00
feat: enhance gateway configuration handling with optional config flag
- Added support for a `--config` flag in the gateway configuration parser to allow absolute or relative paths for the config file. - Improved error handling for determining the config path, ensuring robust loading of `gateway.yaml` from specified locations. - Updated related functions to maintain backward compatibility while enhancing flexibility in configuration management.
This commit is contained in:
parent
a33d03c6b2
commit
b58b632be9
17
CHANGELOG.md
17
CHANGELOG.md
@ -13,6 +13,23 @@ The format is based on [Keep a Changelog][keepachangelog] and adheres to [Semant
|
||||
### Deprecated
|
||||
|
||||
### Fixed
|
||||
## [0.67.5] - 2025-11-11
|
||||
|
||||
### Added
|
||||
- Added `--restart` option to `dbn prod upgrade` to automatically restart services after upgrade.
|
||||
- The gateway now supports an optional `--config` flag to specify the configuration file path.
|
||||
|
||||
### Changed
|
||||
- Improved `dbn prod upgrade` process to better handle existing installations, including detecting node type and ensuring configurations are updated to the latest format.
|
||||
- Configuration loading logic for `node` and `gateway` commands now correctly handles absolute paths passed via command line or systemd.
|
||||
|
||||
### Deprecated
|
||||
|
||||
### Removed
|
||||
|
||||
### Fixed
|
||||
- Fixed an issue during production upgrades where IPFS repositories in private swarms might fail to start due to `AutoConf` not being disabled.
|
||||
|
||||
## [0.67.4] - 2025-11-11
|
||||
|
||||
### Added
|
||||
|
||||
2
Makefile
2
Makefile
@ -19,7 +19,7 @@ test-e2e:
|
||||
|
||||
.PHONY: build clean test run-node run-node2 run-node3 run-example deps tidy fmt vet lint clear-ports install-hooks kill
|
||||
|
||||
VERSION := 0.67.4
|
||||
VERSION := 0.67.5
|
||||
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
|
||||
DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
LDFLAGS := -X 'main.version=$(VERSION)' -X 'main.commit=$(COMMIT)' -X 'main.date=$(DATE)'
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -40,13 +41,35 @@ func getEnvBoolDefault(key string, def bool) bool {
|
||||
}
|
||||
|
||||
// parseGatewayConfig loads gateway.yaml from ~/.debros exclusively.
|
||||
// It accepts an optional --config flag for absolute paths (used by systemd services).
|
||||
func parseGatewayConfig(logger *logging.ColoredLogger) *gateway.Config {
|
||||
// Parse --config flag (optional, for systemd services that pass absolute paths)
|
||||
configFlag := flag.String("config", "", "Config file path (absolute path or filename in ~/.debros)")
|
||||
flag.Parse()
|
||||
|
||||
// Determine config path
|
||||
configPath, err := config.DefaultPath("gateway.yaml")
|
||||
if err != nil {
|
||||
logger.ComponentError(logging.ComponentGeneral, "Failed to determine config path", zap.Error(err))
|
||||
fmt.Fprintf(os.Stderr, "Configuration error: %v\n", err)
|
||||
os.Exit(1)
|
||||
var configPath string
|
||||
var err error
|
||||
if *configFlag != "" {
|
||||
// If --config flag is provided, use it (handles both absolute and relative paths)
|
||||
if filepath.IsAbs(*configFlag) {
|
||||
configPath = *configFlag
|
||||
} else {
|
||||
configPath, err = config.DefaultPath(*configFlag)
|
||||
if err != nil {
|
||||
logger.ComponentError(logging.ComponentGeneral, "Failed to determine config path", zap.Error(err))
|
||||
fmt.Fprintf(os.Stderr, "Configuration error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Default behavior: look for gateway.yaml in ~/.debros/configs/ or ~/.debros/
|
||||
configPath, err = config.DefaultPath("gateway.yaml")
|
||||
if err != nil {
|
||||
logger.ComponentError(logging.ComponentGeneral, "Failed to determine config path", zap.Error(err))
|
||||
fmt.Fprintf(os.Stderr, "Configuration error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Load YAML
|
||||
|
||||
@ -245,11 +245,14 @@ func main() {
|
||||
select_data_dir_check(configName)
|
||||
|
||||
// Determine config path (handle both absolute and relative paths)
|
||||
// Note: select_data_dir_check already validated the path exists, so we can safely determine it here
|
||||
var configPath string
|
||||
var err error
|
||||
if filepath.IsAbs(*configName) {
|
||||
// Absolute path passed directly (e.g., from systemd service)
|
||||
configPath = *configName
|
||||
} else {
|
||||
// Relative path - use DefaultPath which checks both ~/.debros/configs/ and ~/.debros/
|
||||
configPath, err = config.DefaultPath(*configName)
|
||||
if err != nil {
|
||||
logger.Error("Failed to determine config path", zap.Error(err))
|
||||
|
||||
@ -54,6 +54,8 @@ func showProdHelp() {
|
||||
fmt.Printf(" --bootstrap-join ADDR - Bootstrap raft join address (for secondary bootstrap)\n")
|
||||
fmt.Printf(" --domain DOMAIN - Domain for HTTPS (optional)\n")
|
||||
fmt.Printf(" upgrade - Upgrade existing installation (requires root/sudo)\n")
|
||||
fmt.Printf(" Options:\n")
|
||||
fmt.Printf(" --restart - Automatically restart services after upgrade\n")
|
||||
fmt.Printf(" status - Show status of production services\n")
|
||||
fmt.Printf(" logs <service> - View production service logs\n")
|
||||
fmt.Printf(" Options:\n")
|
||||
@ -188,10 +190,14 @@ func handleProdInstall(args []string) {
|
||||
func handleProdUpgrade(args []string) {
|
||||
// Parse arguments
|
||||
force := false
|
||||
restartServices := false
|
||||
for _, arg := range args {
|
||||
if arg == "--force" {
|
||||
force = true
|
||||
}
|
||||
if arg == "--restart" {
|
||||
restartServices = true
|
||||
}
|
||||
}
|
||||
|
||||
if os.Geteuid() != 0 {
|
||||
@ -201,24 +207,108 @@ func handleProdUpgrade(args []string) {
|
||||
|
||||
debrosHome := "/home/debros"
|
||||
fmt.Printf("🔄 Upgrading production installation...\n")
|
||||
fmt.Printf(" This will preserve existing configurations and data\n\n")
|
||||
fmt.Printf(" This will preserve existing configurations and data\n")
|
||||
fmt.Printf(" Configurations will be updated to latest format\n\n")
|
||||
|
||||
// For now, just re-run the install with force flag
|
||||
setup := production.NewProductionSetup(debrosHome, os.Stdout, force)
|
||||
|
||||
// Phase 1: Check prerequisites
|
||||
fmt.Printf("\n📋 Phase 1: Checking prerequisites...\n")
|
||||
if err := setup.Phase1CheckPrerequisites(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "❌ Prerequisites check failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Phase 2: Provision environment (ensures directories exist)
|
||||
fmt.Printf("\n🛠️ Phase 2: Provisioning environment...\n")
|
||||
if err := setup.Phase2ProvisionEnvironment(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "❌ Environment provisioning failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("✅ Upgrade complete!\n")
|
||||
fmt.Printf(" Services will use existing configurations\n")
|
||||
fmt.Printf(" To restart services: sudo systemctl restart debros-*\n\n")
|
||||
// Phase 2b: Install/update binaries
|
||||
fmt.Printf("\nPhase 2b: Installing/updating binaries...\n")
|
||||
if err := setup.Phase2bInstallBinaries(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "❌ Binary installation failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Detect node type from existing installation
|
||||
nodeType := "node"
|
||||
if setup.IsUpdate() {
|
||||
// Check if bootstrap config exists
|
||||
bootstrapConfig := filepath.Join("/home/debros/.debros", "configs", "bootstrap.yaml")
|
||||
if _, err := os.Stat(bootstrapConfig); err == nil {
|
||||
nodeType = "bootstrap"
|
||||
} else {
|
||||
// Check data directory structure
|
||||
bootstrapDataPath := filepath.Join("/home/debros/.debros", "data", "bootstrap")
|
||||
if _, err := os.Stat(bootstrapDataPath); err == nil {
|
||||
nodeType = "bootstrap"
|
||||
}
|
||||
}
|
||||
fmt.Printf(" Detected node type: %s\n", nodeType)
|
||||
} else {
|
||||
fmt.Printf(" ⚠️ No existing installation detected, treating as fresh install\n")
|
||||
fmt.Printf(" Use 'dbn prod install --bootstrap' for fresh bootstrap installation\n")
|
||||
nodeType = "bootstrap" // Default for upgrade if nothing exists
|
||||
}
|
||||
|
||||
// Phase 2c: Ensure services are properly initialized (fixes existing repos)
|
||||
fmt.Printf("\nPhase 2c: Ensuring services are properly initialized...\n")
|
||||
if err := setup.Phase2cInitializeServices(nodeType); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "❌ Service initialization failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Phase 3: Ensure secrets exist (preserves existing secrets)
|
||||
fmt.Printf("\n🔐 Phase 3: Ensuring secrets...\n")
|
||||
if err := setup.Phase3GenerateSecrets(nodeType == "bootstrap"); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "❌ Secret generation failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Phase 4: Regenerate configs (updates to latest format)
|
||||
// Note: This will overwrite existing configs, but preserves secrets
|
||||
bootstrapPeers := []string{} // Could be read from existing config if needed
|
||||
enableHTTPS := false
|
||||
domain := ""
|
||||
bootstrapJoin := ""
|
||||
if err := setup.Phase4GenerateConfigs(nodeType == "bootstrap", bootstrapPeers, "", enableHTTPS, domain, bootstrapJoin); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "⚠️ Config generation warning: %v\n", err)
|
||||
fmt.Fprintf(os.Stderr, " Existing configs preserved\n")
|
||||
}
|
||||
|
||||
// Phase 5: Update systemd services
|
||||
fmt.Printf("\n🔧 Phase 5: Updating systemd services...\n")
|
||||
if err := setup.Phase5CreateSystemdServices(nodeType, ""); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "⚠️ Service update warning: %v\n", err)
|
||||
}
|
||||
|
||||
fmt.Printf("\n✅ Upgrade complete!\n")
|
||||
if restartServices {
|
||||
fmt.Printf(" Restarting services...\n")
|
||||
// Reload systemd daemon
|
||||
exec.Command("systemctl", "daemon-reload").Run()
|
||||
// Restart services to apply changes
|
||||
services := []string{
|
||||
"debros-ipfs-bootstrap",
|
||||
"debros-ipfs-cluster-bootstrap",
|
||||
"debros-rqlite-bootstrap",
|
||||
"debros-olric",
|
||||
"debros-node-bootstrap",
|
||||
"debros-gateway",
|
||||
}
|
||||
for _, svc := range services {
|
||||
exec.Command("systemctl", "restart", svc).Run()
|
||||
}
|
||||
fmt.Printf(" ✓ Services restarted\n")
|
||||
} else {
|
||||
fmt.Printf(" To apply changes, restart services:\n")
|
||||
fmt.Printf(" sudo systemctl daemon-reload\n")
|
||||
fmt.Printf(" sudo systemctl restart debros-*\n")
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
func handleProdStatus() {
|
||||
|
||||
@ -30,7 +30,13 @@ func EnsureConfigDir() (string, error) {
|
||||
// DefaultPath returns the path to the config file for the given component name.
|
||||
// component should be e.g., "node.yaml", "bootstrap.yaml", "gateway.yaml"
|
||||
// It checks both ~/.debros/ and ~/.debros/configs/ for backward compatibility.
|
||||
// If component is already an absolute path, it returns it as-is.
|
||||
func DefaultPath(component string) (string, error) {
|
||||
// If component is already an absolute path, return it directly
|
||||
if filepath.IsAbs(component) {
|
||||
return component, nil
|
||||
}
|
||||
|
||||
dir, err := ConfigDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
@ -340,13 +340,14 @@ func (bi *BinaryInstaller) InstallSystemDependencies() error {
|
||||
// InitializeIPFSRepo initializes an IPFS repository for a node
|
||||
func (bi *BinaryInstaller) InitializeIPFSRepo(nodeType, ipfsRepoPath string, swarmKeyPath string) error {
|
||||
configPath := filepath.Join(ipfsRepoPath, "config")
|
||||
repoExists := false
|
||||
if _, err := os.Stat(configPath); err == nil {
|
||||
// Already initialized
|
||||
return nil
|
||||
repoExists = true
|
||||
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " IPFS repo for %s already exists, ensuring configuration...\n", nodeType)
|
||||
} else {
|
||||
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Initializing IPFS repo for %s...\n", nodeType)
|
||||
}
|
||||
|
||||
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Initializing IPFS repo for %s...\n", nodeType)
|
||||
|
||||
if err := os.MkdirAll(ipfsRepoPath, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create IPFS repo directory: %w", err)
|
||||
}
|
||||
@ -357,25 +358,30 @@ func (bi *BinaryInstaller) InitializeIPFSRepo(nodeType, ipfsRepoPath string, swa
|
||||
return err
|
||||
}
|
||||
|
||||
// Initialize IPFS with the correct repo path
|
||||
cmd := exec.Command(ipfsBinary, "init", "--profile=server", "--repo-dir="+ipfsRepoPath)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to initialize IPFS: %v\n%s", err, string(output))
|
||||
// Initialize IPFS if repo doesn't exist
|
||||
if !repoExists {
|
||||
cmd := exec.Command(ipfsBinary, "init", "--profile=server", "--repo-dir="+ipfsRepoPath)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to initialize IPFS: %v\n%s", err, string(output))
|
||||
}
|
||||
}
|
||||
|
||||
// Copy swarm key if present
|
||||
swarmKeyExists := false
|
||||
if data, err := os.ReadFile(swarmKeyPath); err == nil {
|
||||
if err := os.WriteFile(filepath.Join(ipfsRepoPath, "swarm.key"), data, 0600); err != nil {
|
||||
swarmKeyDest := filepath.Join(ipfsRepoPath, "swarm.key")
|
||||
if err := os.WriteFile(swarmKeyDest, data, 0600); err != nil {
|
||||
return fmt.Errorf("failed to copy swarm key: %w", err)
|
||||
}
|
||||
swarmKeyExists = true
|
||||
}
|
||||
|
||||
// Disable AutoConf for private swarm (required when swarm.key is present)
|
||||
// This prevents IPFS from trying to use the public mainnet AutoConf service
|
||||
// Always disable AutoConf for private swarm when swarm.key is present
|
||||
// This is critical - IPFS will fail to start if AutoConf is enabled on a private network
|
||||
// We do this even for existing repos to fix repos initialized before this fix was applied
|
||||
if swarmKeyExists {
|
||||
cmd = exec.Command(ipfsBinary, "config", "--json", "AutoConf.Enabled", "false")
|
||||
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Disabling AutoConf for private swarm...\n")
|
||||
cmd := exec.Command(ipfsBinary, "config", "--json", "AutoConf.Enabled", "false")
|
||||
cmd.Env = append(os.Environ(), "IPFS_PATH="+ipfsRepoPath)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to disable AutoConf: %v\n%s", err, string(output))
|
||||
@ -390,15 +396,17 @@ func (bi *BinaryInstaller) InitializeIPFSRepo(nodeType, ipfsRepoPath string, swa
|
||||
|
||||
// InitializeIPFSClusterConfig initializes IPFS Cluster configuration
|
||||
// This runs `ipfs-cluster-service init` to create the service.json configuration file.
|
||||
// For existing installations, it ensures the cluster secret is up to date.
|
||||
func (bi *BinaryInstaller) InitializeIPFSClusterConfig(nodeType, clusterPath, clusterSecret string, ipfsAPIPort int) error {
|
||||
serviceJSONPath := filepath.Join(clusterPath, "service.json")
|
||||
configExists := false
|
||||
if _, err := os.Stat(serviceJSONPath); err == nil {
|
||||
// Already initialized
|
||||
return nil
|
||||
configExists = true
|
||||
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " IPFS Cluster config for %s already exists, ensuring it's up to date...\n", nodeType)
|
||||
} else {
|
||||
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Preparing IPFS Cluster path for %s...\n", nodeType)
|
||||
}
|
||||
|
||||
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Preparing IPFS Cluster path for %s...\n", nodeType)
|
||||
|
||||
if err := os.MkdirAll(clusterPath, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create IPFS Cluster directory: %w", err)
|
||||
}
|
||||
@ -412,23 +420,28 @@ func (bi *BinaryInstaller) InitializeIPFSClusterConfig(nodeType, clusterPath, cl
|
||||
return fmt.Errorf("ipfs-cluster-service binary not found: %w", err)
|
||||
}
|
||||
|
||||
// Initialize cluster config with ipfs-cluster-service init
|
||||
// This creates the service.json file with all required sections
|
||||
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Initializing IPFS Cluster config...\n")
|
||||
cmd := exec.Command(clusterBinary, "init", "--force")
|
||||
cmd.Env = append(os.Environ(), "IPFS_CLUSTER_PATH="+clusterPath)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to initialize IPFS Cluster config: %v\n%s", err, string(output))
|
||||
// Initialize cluster config if it doesn't exist
|
||||
if !configExists {
|
||||
// Initialize cluster config with ipfs-cluster-service init
|
||||
// This creates the service.json file with all required sections
|
||||
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Initializing IPFS Cluster config...\n")
|
||||
cmd := exec.Command(clusterBinary, "init", "--force")
|
||||
cmd.Env = append(os.Environ(), "IPFS_CLUSTER_PATH="+clusterPath)
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to initialize IPFS Cluster config: %v\n%s", err, string(output))
|
||||
}
|
||||
}
|
||||
|
||||
// Update the cluster secret in service.json if provided
|
||||
// Always update the cluster secret (for both new and existing configs)
|
||||
// This ensures existing installations get the secret synchronized
|
||||
if clusterSecret != "" {
|
||||
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Updating cluster secret...\n")
|
||||
if err := bi.updateClusterSecret(clusterPath, clusterSecret); err != nil {
|
||||
return fmt.Errorf("failed to update cluster secret: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Fix ownership again after init
|
||||
// Fix ownership again after updates
|
||||
exec.Command("chown", "-R", "debros:debros", clusterPath).Run()
|
||||
|
||||
return nil
|
||||
|
||||
@ -68,6 +68,11 @@ func (ps *ProductionSetup) logf(format string, args ...interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// IsUpdate detects if this is an update to an existing installation
|
||||
func (ps *ProductionSetup) IsUpdate() bool {
|
||||
return ps.stateDetector.IsConfigured() || ps.stateDetector.HasIPFSData()
|
||||
}
|
||||
|
||||
// Phase1CheckPrerequisites performs initial environment validation
|
||||
func (ps *ProductionSetup) Phase1CheckPrerequisites() error {
|
||||
ps.logf("Phase 1: Checking prerequisites...")
|
||||
@ -286,7 +291,12 @@ func (ps *ProductionSetup) Phase3GenerateSecrets(isBootstrap bool) error {
|
||||
|
||||
// Phase4GenerateConfigs generates node, gateway, and service configs
|
||||
func (ps *ProductionSetup) Phase4GenerateConfigs(isBootstrap bool, bootstrapPeers []string, vpsIP string, enableHTTPS bool, domain string, bootstrapJoin string) error {
|
||||
ps.logf("Phase 4: Generating configurations...")
|
||||
if ps.IsUpdate() {
|
||||
ps.logf("Phase 4: Updating configurations...")
|
||||
ps.logf(" (Existing configs will be updated to latest format)")
|
||||
} else {
|
||||
ps.logf("Phase 4: Generating configurations...")
|
||||
}
|
||||
|
||||
// Node config
|
||||
nodeConfig, err := ps.configGenerator.GenerateNodeConfig(isBootstrap, bootstrapPeers, vpsIP, bootstrapJoin)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user