mirror of
https://github.com/DeBrosOfficial/network.git
synced 2025-12-11 09:18:50 +00:00
feat: implement resource validation checks for production deployment
- Added a new ResourceChecker type to validate system resources including disk space, RAM, and CPU cores. - Implemented CheckDiskSpace, CheckRAM, and CheckCPU methods to ensure minimum requirements are met for production deployment. - Integrated resource checks into the ProductionSetup's Phase1CheckPrerequisites method to enhance deployment reliability. - Updated systemd service generation to log output to specific log files instead of the journal for better log management.
This commit is contained in:
parent
52a726ffd4
commit
e9bf94ba96
15
CHANGELOG.md
15
CHANGELOG.md
@ -13,6 +13,21 @@ The format is based on [Keep a Changelog][keepachangelog] and adheres to [Semant
|
||||
### Deprecated
|
||||
|
||||
### Fixed
|
||||
## [0.66.0] - 2025-11-11
|
||||
|
||||
### Added
|
||||
- Pre-installation checks for minimum system resources (10GB disk space, 2GB RAM, 2 CPU cores) are now performed during setup.
|
||||
- All systemd services (IPFS, RQLite, Olric, Node, Gateway) now log directly to dedicated files in the logs directory instead of using the system journal.
|
||||
|
||||
### Changed
|
||||
- Improved logging instructions in the setup completion message to reference the new dedicated log files.
|
||||
|
||||
### Deprecated
|
||||
|
||||
### Removed
|
||||
|
||||
### Fixed
|
||||
\n
|
||||
## [0.65.0] - 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.65.0
|
||||
VERSION := 0.66.0
|
||||
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)'
|
||||
|
||||
@ -5,7 +5,9 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// OSInfo contains detected operating system information
|
||||
@ -213,3 +215,74 @@ func (etc *ExternalToolChecker) CheckGoAvailable() bool {
|
||||
_, err := exec.LookPath("go")
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// ResourceChecker validates system resources for production deployment
|
||||
type ResourceChecker struct{}
|
||||
|
||||
// NewResourceChecker creates a new resource checker
|
||||
func NewResourceChecker() *ResourceChecker {
|
||||
return &ResourceChecker{}
|
||||
}
|
||||
|
||||
// CheckDiskSpace validates sufficient disk space (minimum 10GB free)
|
||||
func (rc *ResourceChecker) CheckDiskSpace(path string) error {
|
||||
var stat syscall.Statfs_t
|
||||
if err := syscall.Statfs(path, &stat); err != nil {
|
||||
return fmt.Errorf("failed to check disk space: %w", err)
|
||||
}
|
||||
|
||||
// Available space in bytes
|
||||
availableBytes := stat.Bavail * uint64(stat.Bsize)
|
||||
minRequiredBytes := uint64(10 * 1024 * 1024 * 1024) // 10GB
|
||||
|
||||
if availableBytes < minRequiredBytes {
|
||||
availableGB := float64(availableBytes) / (1024 * 1024 * 1024)
|
||||
return fmt.Errorf("insufficient disk space: %.1fGB available, minimum 10GB required", availableGB)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckRAM validates sufficient RAM (minimum 2GB total)
|
||||
func (rc *ResourceChecker) CheckRAM() error {
|
||||
data, err := os.ReadFile("/proc/meminfo")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read memory info: %w", err)
|
||||
}
|
||||
|
||||
lines := strings.Split(string(data), "\n")
|
||||
totalKB := uint64(0)
|
||||
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "MemTotal:") {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) >= 2 {
|
||||
if kb, err := strconv.ParseUint(parts[1], 10, 64); err == nil {
|
||||
totalKB = kb
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if totalKB == 0 {
|
||||
return fmt.Errorf("could not determine total RAM")
|
||||
}
|
||||
|
||||
minRequiredKB := uint64(2 * 1024 * 1024) // 2GB in KB
|
||||
if totalKB < minRequiredKB {
|
||||
totalGB := float64(totalKB) / (1024 * 1024)
|
||||
return fmt.Errorf("insufficient RAM: %.1fGB total, minimum 2GB required", totalGB)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckCPU validates sufficient CPU cores (minimum 2 cores)
|
||||
func (rc *ResourceChecker) CheckCPU() error {
|
||||
cores := runtime.NumCPU()
|
||||
if cores < 2 {
|
||||
return fmt.Errorf("insufficient CPU cores: %d available, minimum 2 required", cores)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ type ProductionSetup struct {
|
||||
privChecker *PrivilegeChecker
|
||||
osDetector *OSDetector
|
||||
archDetector *ArchitectureDetector
|
||||
resourceChecker *ResourceChecker
|
||||
fsProvisioner *FilesystemProvisioner
|
||||
userProvisioner *UserProvisioner
|
||||
stateDetector *StateDetector
|
||||
@ -48,6 +49,7 @@ func NewProductionSetup(debrosHome string, logWriter io.Writer, forceReconfigure
|
||||
privChecker: &PrivilegeChecker{},
|
||||
osDetector: &OSDetector{},
|
||||
archDetector: &ArchitectureDetector{},
|
||||
resourceChecker: NewResourceChecker(),
|
||||
fsProvisioner: NewFilesystemProvisioner(debrosHome),
|
||||
userProvisioner: NewUserProvisioner("debros", debrosHome, "/bin/bash"),
|
||||
stateDetector: NewStateDetector(debrosDir),
|
||||
@ -115,6 +117,25 @@ func (ps *ProductionSetup) Phase1CheckPrerequisites() error {
|
||||
}
|
||||
ps.logf(" ✓ Basic dependencies available")
|
||||
|
||||
// Check system resources
|
||||
if err := ps.resourceChecker.CheckDiskSpace(ps.debrosHome); err != nil {
|
||||
ps.logf(" ❌ %v", err)
|
||||
return err
|
||||
}
|
||||
ps.logf(" ✓ Sufficient disk space available")
|
||||
|
||||
if err := ps.resourceChecker.CheckRAM(); err != nil {
|
||||
ps.logf(" ❌ %v", err)
|
||||
return err
|
||||
}
|
||||
ps.logf(" ✓ Sufficient RAM available")
|
||||
|
||||
if err := ps.resourceChecker.CheckCPU(); err != nil {
|
||||
ps.logf(" ❌ %v", err)
|
||||
return err
|
||||
}
|
||||
ps.logf(" ✓ Sufficient CPU cores available")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -426,8 +447,15 @@ func (ps *ProductionSetup) LogSetupComplete(peerID string) {
|
||||
ps.logf("\nNode Peer ID: %s", peerID)
|
||||
ps.logf("\nService Management:")
|
||||
ps.logf(" systemctl status debros-ipfs-bootstrap")
|
||||
ps.logf(" systemctl logs debros-node-bootstrap")
|
||||
ps.logf(" sudo tail -f %s/logs/node.log", ps.debrosDir)
|
||||
ps.logf(" journalctl -u debros-node-bootstrap -f")
|
||||
ps.logf(" tail -f %s/logs/node-bootstrap.log", ps.debrosDir)
|
||||
ps.logf("\nLog Files:")
|
||||
ps.logf(" %s/logs/ipfs-bootstrap.log", ps.debrosDir)
|
||||
ps.logf(" %s/logs/ipfs-cluster-bootstrap.log", ps.debrosDir)
|
||||
ps.logf(" %s/logs/rqlite-bootstrap.log", ps.debrosDir)
|
||||
ps.logf(" %s/logs/olric.log", ps.debrosDir)
|
||||
ps.logf(" %s/logs/node-bootstrap.log", ps.debrosDir)
|
||||
ps.logf(" %s/logs/gateway.log", ps.debrosDir)
|
||||
ps.logf("\nStart All Services:")
|
||||
ps.logf(" systemctl start debros-ipfs-bootstrap debros-ipfs-cluster-bootstrap debros-rqlite-bootstrap debros-olric debros-node-bootstrap debros-gateway")
|
||||
ps.logf("\nVerify Installation:")
|
||||
|
||||
@ -31,6 +31,8 @@ func (ssg *SystemdServiceGenerator) GenerateIPFSService(nodeType string) string
|
||||
ipfsRepoPath = filepath.Join(ssg.debrosDir, "data", "node", "ipfs", "repo")
|
||||
}
|
||||
|
||||
logFile := filepath.Join(ssg.debrosDir, "logs", fmt.Sprintf("ipfs-%s.log", nodeType))
|
||||
|
||||
return fmt.Sprintf(`[Unit]
|
||||
Description=IPFS Daemon (%s)
|
||||
After=network-online.target
|
||||
@ -46,8 +48,8 @@ ExecStartPre=/bin/bash -c 'if [ -f %s/secrets/swarm.key ] && [ ! -f %s/swarm.key
|
||||
ExecStart=/usr/bin/ipfs daemon --enable-pubsub-experiment --repo-dir=%s
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
StandardOutput=file:%s
|
||||
StandardError=file:%s
|
||||
SyslogIdentifier=ipfs-%s
|
||||
|
||||
NoNewPrivileges=yes
|
||||
@ -57,7 +59,7 @@ ReadWritePaths=%s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
`, nodeType, ssg.debrosHome, ipfsRepoPath, ssg.debrosDir, ipfsRepoPath, ssg.debrosDir, ipfsRepoPath, ipfsRepoPath, ipfsRepoPath, nodeType, ssg.debrosDir)
|
||||
`, nodeType, ssg.debrosHome, ipfsRepoPath, ssg.debrosDir, ipfsRepoPath, ssg.debrosDir, ipfsRepoPath, ipfsRepoPath, ipfsRepoPath, logFile, logFile, nodeType, ssg.debrosDir)
|
||||
}
|
||||
|
||||
// GenerateIPFSClusterService generates the IPFS Cluster systemd unit
|
||||
@ -69,6 +71,8 @@ func (ssg *SystemdServiceGenerator) GenerateIPFSClusterService(nodeType string)
|
||||
clusterPath = filepath.Join(ssg.debrosDir, "data", "node", "ipfs-cluster")
|
||||
}
|
||||
|
||||
logFile := filepath.Join(ssg.debrosDir, "logs", fmt.Sprintf("ipfs-cluster-%s.log", nodeType))
|
||||
|
||||
return fmt.Sprintf(`[Unit]
|
||||
Description=IPFS Cluster Service (%s)
|
||||
After=debros-ipfs-%s.service
|
||||
@ -85,8 +89,8 @@ Environment=IPFS_CLUSTER_PATH=%s
|
||||
ExecStart=/usr/local/bin/ipfs-cluster-service daemon
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
StandardOutput=file:%s
|
||||
StandardError=file:%s
|
||||
SyslogIdentifier=ipfs-cluster-%s
|
||||
|
||||
NoNewPrivileges=yes
|
||||
@ -96,7 +100,7 @@ ReadWritePaths=%s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
`, nodeType, nodeType, nodeType, nodeType, ssg.debrosHome, ssg.debrosHome, clusterPath, nodeType, ssg.debrosDir)
|
||||
`, nodeType, nodeType, nodeType, nodeType, ssg.debrosHome, ssg.debrosHome, clusterPath, logFile, logFile, nodeType, ssg.debrosDir)
|
||||
}
|
||||
|
||||
// GenerateRQLiteService generates the RQLite systemd unit
|
||||
@ -119,6 +123,8 @@ func (ssg *SystemdServiceGenerator) GenerateRQLiteService(nodeType string, httpP
|
||||
|
||||
args += fmt.Sprintf(` %s`, dataDir)
|
||||
|
||||
logFile := filepath.Join(ssg.debrosDir, "logs", fmt.Sprintf("rqlite-%s.log", nodeType))
|
||||
|
||||
return fmt.Sprintf(`[Unit]
|
||||
Description=RQLite Database (%s)
|
||||
After=network-online.target
|
||||
@ -132,8 +138,8 @@ Environment=HOME=%s
|
||||
ExecStart=/usr/local/bin/rqlited %s
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
StandardOutput=file:%s
|
||||
StandardError=file:%s
|
||||
SyslogIdentifier=rqlite-%s
|
||||
|
||||
NoNewPrivileges=yes
|
||||
@ -143,12 +149,13 @@ ReadWritePaths=%s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
`, nodeType, ssg.debrosHome, args, nodeType, ssg.debrosDir)
|
||||
`, nodeType, ssg.debrosHome, args, logFile, logFile, nodeType, ssg.debrosDir)
|
||||
}
|
||||
|
||||
// GenerateOlricService generates the Olric systemd unit
|
||||
func (ssg *SystemdServiceGenerator) GenerateOlricService() string {
|
||||
olricConfigPath := filepath.Join(ssg.debrosDir, "configs", "olric", "config.yaml")
|
||||
logFile := filepath.Join(ssg.debrosDir, "logs", "olric.log")
|
||||
|
||||
return fmt.Sprintf(`[Unit]
|
||||
Description=Olric Cache Server
|
||||
@ -164,8 +171,8 @@ Environment=OLRIC_SERVER_CONFIG=%s
|
||||
ExecStart=/usr/local/bin/olric-server
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
StandardOutput=file:%s
|
||||
StandardError=file:%s
|
||||
SyslogIdentifier=olric
|
||||
|
||||
NoNewPrivileges=yes
|
||||
@ -175,7 +182,7 @@ ReadWritePaths=%s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
`, ssg.debrosHome, olricConfigPath, ssg.debrosDir)
|
||||
`, ssg.debrosHome, olricConfigPath, logFile, logFile, ssg.debrosDir)
|
||||
}
|
||||
|
||||
// GenerateNodeService generates the DeBros Node systemd unit
|
||||
@ -187,6 +194,8 @@ func (ssg *SystemdServiceGenerator) GenerateNodeService(nodeType string) string
|
||||
configFile = "node.yaml"
|
||||
}
|
||||
|
||||
logFile := filepath.Join(ssg.debrosDir, "logs", fmt.Sprintf("node-%s.log", nodeType))
|
||||
|
||||
return fmt.Sprintf(`[Unit]
|
||||
Description=DeBros Network Node (%s)
|
||||
After=debros-ipfs-cluster-%s.service
|
||||
@ -202,8 +211,8 @@ Environment=HOME=%s
|
||||
ExecStart=%s/bin/node --config %s/configs/%s
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
StandardOutput=file:%s
|
||||
StandardError=file:%s
|
||||
SyslogIdentifier=debros-node-%s
|
||||
|
||||
NoNewPrivileges=yes
|
||||
@ -213,12 +222,13 @@ ReadWritePaths=%s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
`, nodeType, nodeType, nodeType, nodeType, ssg.debrosHome, ssg.debrosHome, ssg.debrosHome, ssg.debrosDir, configFile, nodeType, ssg.debrosDir)
|
||||
`, nodeType, nodeType, nodeType, nodeType, ssg.debrosHome, ssg.debrosHome, ssg.debrosHome, ssg.debrosDir, configFile, logFile, logFile, nodeType, ssg.debrosDir)
|
||||
}
|
||||
|
||||
// GenerateGatewayService generates the DeBros Gateway systemd unit
|
||||
func (ssg *SystemdServiceGenerator) GenerateGatewayService(nodeType string) string {
|
||||
nodeService := fmt.Sprintf("debros-node-%s.service", nodeType)
|
||||
logFile := filepath.Join(ssg.debrosDir, "logs", "gateway.log")
|
||||
return fmt.Sprintf(`[Unit]
|
||||
Description=DeBros Gateway
|
||||
After=%s
|
||||
@ -233,8 +243,8 @@ Environment=HOME=%s
|
||||
ExecStart=%s/bin/gateway --config %s/configs/gateway.yaml
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
StandardOutput=file:%s
|
||||
StandardError=file:%s
|
||||
SyslogIdentifier=debros-gateway
|
||||
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
@ -247,7 +257,7 @@ ReadWritePaths=%s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
`, nodeService, nodeService, ssg.debrosHome, ssg.debrosHome, ssg.debrosHome, ssg.debrosDir, ssg.debrosDir)
|
||||
`, nodeService, nodeService, ssg.debrosHome, ssg.debrosHome, ssg.debrosHome, ssg.debrosDir, logFile, logFile, ssg.debrosDir)
|
||||
}
|
||||
|
||||
// SystemdController manages systemd service operations
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user