mirror of
https://github.com/DeBrosOfficial/network.git
synced 2025-12-11 10:18:50 +00:00
refactor: reorder production installation phases and enhance service initialization
- Adjusted the installation sequence to generate secrets before initializing services, ensuring necessary keys are in place. - Updated service initialization to account for both bootstrap and node variants, improving service status reporting. - Enhanced error handling during IPFS repo and cluster path initialization, providing clearer feedback on failures. - Captured the node peer ID for logging after secret generation, improving visibility during production setup.
This commit is contained in:
parent
6a86592cad
commit
17fc78975d
16
CHANGELOG.md
16
CHANGELOG.md
@ -13,6 +13,22 @@ The format is based on [Keep a Changelog][keepachangelog] and adheres to [Semant
|
|||||||
### Deprecated
|
### Deprecated
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
## [0.62.0] - 2025-11-10
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- The `prod status` command now correctly checks for both 'bootstrap' and 'node' service variants.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- The production installation process now generates secrets (like the cluster secret and peer ID) before initializing services. This ensures all necessary secrets are available when services start.
|
||||||
|
- The `prod install` command now displays the actual Peer ID upon completion instead of a placeholder.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixed an issue where IPFS Cluster initialization was using a hardcoded configuration file instead of relying on the standard `ipfs-cluster-service init` process.
|
||||||
|
|
||||||
## [0.61.0] - 2025-11-10
|
## [0.61.0] - 2025-11-10
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
2
Makefile
2
Makefile
@ -21,7 +21,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
|
.PHONY: build clean test run-node run-node2 run-node3 run-example deps tidy fmt vet lint clear-ports install-hooks kill
|
||||||
|
|
||||||
VERSION := 0.61.0
|
VERSION := 0.62.0
|
||||||
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
|
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
|
||||||
DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
|
DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||||
LDFLAGS := -X 'main.version=$(VERSION)' -X 'main.commit=$(COMMIT)' -X 'main.date=$(DATE)'
|
LDFLAGS := -X 'main.version=$(VERSION)' -X 'main.commit=$(COMMIT)' -X 'main.date=$(DATE)'
|
||||||
|
|||||||
@ -128,24 +128,27 @@ func handleProdInstall(args []string) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 2c: Initialize services
|
// Determine node type early
|
||||||
nodeType := "node"
|
nodeType := "node"
|
||||||
if isBootstrap {
|
if isBootstrap {
|
||||||
nodeType = "bootstrap"
|
nodeType = "bootstrap"
|
||||||
}
|
}
|
||||||
fmt.Printf("\nPhase 2c: Initializing services...\n")
|
|
||||||
if err := setup.Phase2cInitializeServices(nodeType); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "❌ Service initialization failed: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Phase 3: Generate secrets
|
// Phase 3: Generate secrets FIRST (before service initialization)
|
||||||
|
// This ensures cluster secret and swarm key exist before repos are seeded
|
||||||
fmt.Printf("\n🔐 Phase 3: Generating secrets...\n")
|
fmt.Printf("\n🔐 Phase 3: Generating secrets...\n")
|
||||||
if err := setup.Phase3GenerateSecrets(isBootstrap); err != nil {
|
if err := setup.Phase3GenerateSecrets(isBootstrap); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "❌ Secret generation failed: %v\n", err)
|
fmt.Fprintf(os.Stderr, "❌ Secret generation failed: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Phase 2c: Initialize services (after secrets are in place)
|
||||||
|
fmt.Printf("\nPhase 2c: Initializing services...\n")
|
||||||
|
if err := setup.Phase2cInitializeServices(nodeType); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "❌ Service initialization failed: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
// Phase 4: Generate configs
|
// Phase 4: Generate configs
|
||||||
fmt.Printf("\n⚙️ Phase 4: Generating configurations...\n")
|
fmt.Printf("\n⚙️ Phase 4: Generating configurations...\n")
|
||||||
enableHTTPS := domain != ""
|
enableHTTPS := domain != ""
|
||||||
@ -161,8 +164,8 @@ func handleProdInstall(args []string) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log completion
|
// Log completion with actual peer ID
|
||||||
setup.LogSetupComplete("< peer ID from config >")
|
setup.LogSetupComplete(setup.NodePeerID)
|
||||||
fmt.Printf("✅ Production installation complete!\n\n")
|
fmt.Printf("✅ Production installation complete!\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,27 +208,49 @@ func handleProdUpgrade(args []string) {
|
|||||||
func handleProdStatus() {
|
func handleProdStatus() {
|
||||||
fmt.Printf("Production Environment Status\n\n")
|
fmt.Printf("Production Environment Status\n\n")
|
||||||
|
|
||||||
servicesList := []struct {
|
// Check for all possible service names (bootstrap and node variants)
|
||||||
name string
|
serviceNames := []string{
|
||||||
desc string
|
"debros-ipfs-bootstrap",
|
||||||
}{
|
"debros-ipfs-node",
|
||||||
{"debros-ipfs-bootstrap", "IPFS Daemon (Bootstrap)"},
|
"debros-ipfs-cluster-bootstrap",
|
||||||
{"debros-ipfs-cluster-bootstrap", "IPFS Cluster (Bootstrap)"},
|
"debros-ipfs-cluster-node",
|
||||||
{"debros-rqlite-bootstrap", "RQLite Database (Bootstrap)"},
|
"debros-rqlite-bootstrap",
|
||||||
{"debros-olric", "Olric Cache Server"},
|
"debros-rqlite-node",
|
||||||
{"debros-node-bootstrap", "DeBros Node (Bootstrap)"},
|
"debros-olric",
|
||||||
{"debros-gateway", "DeBros Gateway"},
|
"debros-node-bootstrap",
|
||||||
|
"debros-node-node",
|
||||||
|
"debros-gateway",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Friendly descriptions
|
||||||
|
descriptions := map[string]string{
|
||||||
|
"debros-ipfs-bootstrap": "IPFS Daemon (Bootstrap)",
|
||||||
|
"debros-ipfs-node": "IPFS Daemon (Node)",
|
||||||
|
"debros-ipfs-cluster-bootstrap": "IPFS Cluster (Bootstrap)",
|
||||||
|
"debros-ipfs-cluster-node": "IPFS Cluster (Node)",
|
||||||
|
"debros-rqlite-bootstrap": "RQLite Database (Bootstrap)",
|
||||||
|
"debros-rqlite-node": "RQLite Database (Node)",
|
||||||
|
"debros-olric": "Olric Cache Server",
|
||||||
|
"debros-node-bootstrap": "DeBros Node (Bootstrap)",
|
||||||
|
"debros-node-node": "DeBros Node (Node)",
|
||||||
|
"debros-gateway": "DeBros Gateway",
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Services:\n")
|
fmt.Printf("Services:\n")
|
||||||
for _, svc := range servicesList {
|
found := false
|
||||||
cmd := "systemctl"
|
for _, svc := range serviceNames {
|
||||||
err := exec.Command(cmd, "is-active", "--quiet", svc.name).Run()
|
cmd := exec.Command("systemctl", "is-active", "--quiet", svc)
|
||||||
|
err := cmd.Run()
|
||||||
status := "❌ Inactive"
|
status := "❌ Inactive"
|
||||||
if err == nil {
|
if err == nil {
|
||||||
status = "✅ Active"
|
status = "✅ Active"
|
||||||
|
found = true
|
||||||
}
|
}
|
||||||
fmt.Printf(" %s: %s\n", status, svc.desc)
|
fmt.Printf(" %s: %s\n", status, descriptions[svc])
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
fmt.Printf(" (No services found - installation may be incomplete)\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("\nDirectories:\n")
|
fmt.Printf("\nDirectories:\n")
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// BinaryInstaller handles downloading and installing external binaries
|
// BinaryInstaller handles downloading and installing external binaries
|
||||||
@ -227,9 +226,11 @@ func (bi *BinaryInstaller) InitializeIPFSRepo(nodeType, ipfsRepoPath string, swa
|
|||||||
|
|
||||||
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)
|
||||||
|
|
||||||
os.MkdirAll(ipfsRepoPath, 0755)
|
if err := os.MkdirAll(ipfsRepoPath, 0755); err != nil {
|
||||||
|
return fmt.Errorf("failed to create IPFS repo directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize IPFS
|
// Initialize IPFS with the correct repo path
|
||||||
cmd := exec.Command("ipfs", "init", "--profile=server", "--repo-dir="+ipfsRepoPath)
|
cmd := exec.Command("ipfs", "init", "--profile=server", "--repo-dir="+ipfsRepoPath)
|
||||||
if output, err := cmd.CombinedOutput(); err != nil {
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
return fmt.Errorf("failed to initialize IPFS: %v\n%s", err, string(output))
|
return fmt.Errorf("failed to initialize IPFS: %v\n%s", err, string(output))
|
||||||
@ -237,13 +238,20 @@ func (bi *BinaryInstaller) InitializeIPFSRepo(nodeType, ipfsRepoPath string, swa
|
|||||||
|
|
||||||
// Copy swarm key if present
|
// Copy swarm key if present
|
||||||
if data, err := os.ReadFile(swarmKeyPath); err == nil {
|
if data, err := os.ReadFile(swarmKeyPath); err == nil {
|
||||||
os.WriteFile(filepath.Join(ipfsRepoPath, "swarm.key"), data, 0600)
|
if err := os.WriteFile(filepath.Join(ipfsRepoPath, "swarm.key"), data, 0600); err != nil {
|
||||||
|
return fmt.Errorf("failed to copy swarm key: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fix ownership
|
||||||
|
exec.Command("chown", "-R", "debros:debros", ipfsRepoPath).Run()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitializeIPFSClusterConfig initializes IPFS Cluster configuration
|
// InitializeIPFSClusterConfig initializes IPFS Cluster configuration
|
||||||
|
// Note: This is a placeholder config. The full initialization will occur via `ipfs-cluster-service init`
|
||||||
|
// which is run during Phase2cInitializeServices with the IPFS_CLUSTER_PATH env var set.
|
||||||
func (bi *BinaryInstaller) InitializeIPFSClusterConfig(nodeType, clusterPath, clusterSecret string, ipfsAPIPort int) error {
|
func (bi *BinaryInstaller) InitializeIPFSClusterConfig(nodeType, clusterPath, clusterSecret string, ipfsAPIPort int) error {
|
||||||
serviceJSONPath := filepath.Join(clusterPath, "service.json")
|
serviceJSONPath := filepath.Join(clusterPath, "service.json")
|
||||||
if _, err := os.Stat(serviceJSONPath); err == nil {
|
if _, err := os.Stat(serviceJSONPath); err == nil {
|
||||||
@ -251,43 +259,10 @@ func (bi *BinaryInstaller) InitializeIPFSClusterConfig(nodeType, clusterPath, cl
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Initializing IPFS Cluster config for %s...\n", nodeType)
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Preparing IPFS Cluster path for %s...\n", nodeType)
|
||||||
|
|
||||||
os.MkdirAll(clusterPath, 0755)
|
if err := os.MkdirAll(clusterPath, 0755); err != nil {
|
||||||
|
return fmt.Errorf("failed to create IPFS Cluster directory: %w", err)
|
||||||
// For now, just create a minimal service.json
|
|
||||||
// This will be properly configured during service startup
|
|
||||||
cfgContent := fmt.Sprintf(`{
|
|
||||||
"cluster": {
|
|
||||||
"peername": "%s",
|
|
||||||
"secret": "%s",
|
|
||||||
"listen_multiaddress": ["/ip4/0.0.0.0/tcp/9096"],
|
|
||||||
"leave_on_shutdown": false
|
|
||||||
},
|
|
||||||
"api": {
|
|
||||||
"restapi": {
|
|
||||||
"http_listen_multiaddress": "/ip4/0.0.0.0/tcp/9094"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ipfs_connector": {
|
|
||||||
"ipfshttp": {
|
|
||||||
"node_multiaddress": "/ip4/127.0.0.1/tcp/%d"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"consensus": {
|
|
||||||
"crdt": {
|
|
||||||
"cluster_name": "debros",
|
|
||||||
"trusted_peers": ["*"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"datastore": {
|
|
||||||
"type": "badger",
|
|
||||||
"path": "%s/badger"
|
|
||||||
}
|
|
||||||
}`, nodeType, strings.TrimSpace(clusterSecret), ipfsAPIPort, clusterPath)
|
|
||||||
|
|
||||||
if err := os.WriteFile(serviceJSONPath, []byte(cfgContent), 0644); err != nil {
|
|
||||||
return fmt.Errorf("failed to write cluster config: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exec.Command("chown", "-R", "debros:debros", clusterPath).Run()
|
exec.Command("chown", "-R", "debros:debros", clusterPath).Run()
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ type ProductionSetup struct {
|
|||||||
serviceController *SystemdController
|
serviceController *SystemdController
|
||||||
binaryInstaller *BinaryInstaller
|
binaryInstaller *BinaryInstaller
|
||||||
branch string
|
branch string
|
||||||
|
NodePeerID string // Captured during Phase3 for later display
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProductionSetup creates a new production setup orchestrator
|
// NewProductionSetup creates a new production setup orchestrator
|
||||||
@ -199,27 +201,23 @@ func (ps *ProductionSetup) Phase2bInstallBinaries() error {
|
|||||||
func (ps *ProductionSetup) Phase2cInitializeServices(nodeType string) error {
|
func (ps *ProductionSetup) Phase2cInitializeServices(nodeType string) error {
|
||||||
ps.logf("Phase 2c: Initializing services...")
|
ps.logf("Phase 2c: Initializing services...")
|
||||||
|
|
||||||
// Get cluster secret for IPFS
|
// Build paths with nodeType awareness to match systemd unit definitions
|
||||||
clusterSecret, err := os.ReadFile(ps.debrosDir + "/secrets/cluster-secret")
|
dataDir := filepath.Join(ps.debrosDir, "data", nodeType)
|
||||||
if err != nil {
|
|
||||||
clusterSecret = []byte("")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize IPFS repo
|
// Initialize IPFS repo with correct path structure
|
||||||
ipfsRepoPath := ps.debrosDir + "/data/ipfs"
|
ipfsRepoPath := filepath.Join(dataDir, "ipfs", "repo")
|
||||||
if err := ps.binaryInstaller.InitializeIPFSRepo(nodeType, ipfsRepoPath, ps.debrosDir+"/secrets/swarm.key"); err != nil {
|
if err := ps.binaryInstaller.InitializeIPFSRepo(nodeType, ipfsRepoPath, filepath.Join(ps.debrosDir, "secrets", "swarm.key")); err != nil {
|
||||||
ps.logf(" ⚠️ IPFS initialization warning: %v", err)
|
ps.logf(" ⚠️ IPFS initialization warning: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize IPFS Cluster config
|
// Initialize IPFS Cluster path (just ensure directory exists, actual init happens in daemon startup)
|
||||||
clusterPath := ps.debrosDir + "/data/ipfs-cluster"
|
clusterPath := filepath.Join(dataDir, "ipfs-cluster")
|
||||||
ipfsAPIPort := 4501
|
if err := ps.binaryInstaller.InitializeIPFSClusterConfig(nodeType, clusterPath, "", 4501); err != nil {
|
||||||
if err := ps.binaryInstaller.InitializeIPFSClusterConfig(nodeType, clusterPath, string(clusterSecret), ipfsAPIPort); err != nil {
|
|
||||||
ps.logf(" ⚠️ IPFS Cluster initialization warning: %v", err)
|
ps.logf(" ⚠️ IPFS Cluster initialization warning: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize RQLite data directory
|
// Initialize RQLite data directory
|
||||||
rqliteDataDir := ps.debrosDir + "/data/rqlite"
|
rqliteDataDir := filepath.Join(dataDir, "rqlite")
|
||||||
if err := ps.binaryInstaller.InitializeRQLiteDataDir(nodeType, rqliteDataDir); err != nil {
|
if err := ps.binaryInstaller.InitializeRQLiteDataDir(nodeType, rqliteDataDir); err != nil {
|
||||||
ps.logf(" ⚠️ RQLite initialization warning: %v", err)
|
ps.logf(" ⚠️ RQLite initialization warning: %v", err)
|
||||||
}
|
}
|
||||||
@ -254,7 +252,9 @@ func (ps *ProductionSetup) Phase3GenerateSecrets(isBootstrap bool) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to ensure node identity: %w", err)
|
return fmt.Errorf("failed to ensure node identity: %w", err)
|
||||||
}
|
}
|
||||||
ps.logf(" ✓ Node identity ensured (Peer ID: %s)", peerID.String())
|
peerIDStr := peerID.String()
|
||||||
|
ps.NodePeerID = peerIDStr // Capture for later display
|
||||||
|
ps.logf(" ✓ Node identity ensured (Peer ID: %s)", peerIDStr)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -81,8 +81,8 @@ User=debros
|
|||||||
Group=debros
|
Group=debros
|
||||||
WorkingDirectory=%s
|
WorkingDirectory=%s
|
||||||
Environment=HOME=%s
|
Environment=HOME=%s
|
||||||
Environment=CLUSTER_PATH=%s
|
Environment=IPFS_CLUSTER_PATH=%s
|
||||||
ExecStart=/usr/local/bin/ipfs-cluster-service daemon --config %s/service.json
|
ExecStart=/usr/local/bin/ipfs-cluster-service daemon
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
StandardOutput=journal
|
StandardOutput=journal
|
||||||
@ -96,7 +96,7 @@ ReadWritePaths=%s
|
|||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
`, nodeType, nodeType, nodeType, nodeType, ssg.debrosHome, ssg.debrosHome, clusterPath, clusterPath, nodeType, ssg.debrosDir)
|
`, nodeType, nodeType, nodeType, nodeType, ssg.debrosHome, ssg.debrosHome, clusterPath, nodeType, ssg.debrosDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateRQLiteService generates the RQLite systemd unit
|
// GenerateRQLiteService generates the RQLite systemd unit
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user