mirror of
https://github.com/DeBrosOfficial/network.git
synced 2025-12-12 23:18:49 +00:00
- Improved the binary installation process by checking if the git repository is already initialized, allowing for updates instead of re-cloning. - Added error handling for fetching and resetting the repository to ensure the latest changes are applied. - Enhanced IPFS configuration management by clearing AutoConf placeholders to prevent startup errors, with detailed logging for each cleanup step.
542 lines
20 KiB
Go
542 lines
20 KiB
Go
package production
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// BinaryInstaller handles downloading and installing external binaries
|
|
type BinaryInstaller struct {
|
|
arch string
|
|
logWriter interface{} // io.Writer
|
|
}
|
|
|
|
// NewBinaryInstaller creates a new binary installer
|
|
func NewBinaryInstaller(arch string, logWriter interface{}) *BinaryInstaller {
|
|
return &BinaryInstaller{
|
|
arch: arch,
|
|
logWriter: logWriter,
|
|
}
|
|
}
|
|
|
|
// InstallRQLite downloads and installs RQLite
|
|
func (bi *BinaryInstaller) InstallRQLite() error {
|
|
if _, err := exec.LookPath("rqlited"); err == nil {
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " ✓ RQLite already installed\n")
|
|
return nil
|
|
}
|
|
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Installing RQLite...\n")
|
|
|
|
version := "8.43.0"
|
|
tarball := fmt.Sprintf("rqlite-v%s-linux-%s.tar.gz", version, bi.arch)
|
|
url := fmt.Sprintf("https://github.com/rqlite/rqlite/releases/download/v%s/%s", version, tarball)
|
|
|
|
// Download
|
|
cmd := exec.Command("wget", "-q", url, "-O", "/tmp/"+tarball)
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("failed to download RQLite: %w", err)
|
|
}
|
|
|
|
// Extract
|
|
cmd = exec.Command("tar", "-C", "/tmp", "-xzf", "/tmp/"+tarball)
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("failed to extract RQLite: %w", err)
|
|
}
|
|
|
|
// Copy binaries
|
|
dir := fmt.Sprintf("/tmp/rqlite-v%s-linux-%s", version, bi.arch)
|
|
if err := exec.Command("cp", dir+"/rqlited", "/usr/local/bin/").Run(); err != nil {
|
|
return fmt.Errorf("failed to copy rqlited binary: %w", err)
|
|
}
|
|
exec.Command("chmod", "+x", "/usr/local/bin/rqlited").Run()
|
|
|
|
// Ensure PATH includes /usr/local/bin
|
|
os.Setenv("PATH", os.Getenv("PATH")+":/usr/local/bin")
|
|
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " ✓ RQLite installed\n")
|
|
return nil
|
|
}
|
|
|
|
// InstallIPFS downloads and installs IPFS (Kubo)
|
|
// Follows official steps from https://docs.ipfs.tech/install/command-line/
|
|
func (bi *BinaryInstaller) InstallIPFS() error {
|
|
if _, err := exec.LookPath("ipfs"); err == nil {
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " ✓ IPFS already installed\n")
|
|
return nil
|
|
}
|
|
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Installing IPFS (Kubo)...\n")
|
|
|
|
// Follow official installation steps in order
|
|
kuboVersion := "v0.38.2"
|
|
tarball := fmt.Sprintf("kubo_%s_linux-%s.tar.gz", kuboVersion, bi.arch)
|
|
url := fmt.Sprintf("https://dist.ipfs.tech/kubo/%s/%s", kuboVersion, tarball)
|
|
tmpDir := "/tmp"
|
|
tarPath := filepath.Join(tmpDir, tarball)
|
|
kuboDir := filepath.Join(tmpDir, "kubo")
|
|
|
|
// Step 1: Download the Linux binary from dist.ipfs.tech
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Step 1: Downloading Kubo v%s...\n", kuboVersion)
|
|
cmd := exec.Command("wget", "-q", url, "-O", tarPath)
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("failed to download kubo from %s: %w", url, err)
|
|
}
|
|
|
|
// Verify tarball exists
|
|
if _, err := os.Stat(tarPath); err != nil {
|
|
return fmt.Errorf("kubo tarball not found after download at %s: %w", tarPath, err)
|
|
}
|
|
|
|
// Step 2: Unzip the file
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Step 2: Extracting Kubo archive...\n")
|
|
cmd = exec.Command("tar", "-xzf", tarPath, "-C", tmpDir)
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("failed to extract kubo tarball: %w", err)
|
|
}
|
|
|
|
// Verify extraction
|
|
if _, err := os.Stat(kuboDir); err != nil {
|
|
return fmt.Errorf("kubo directory not found after extraction at %s: %w", kuboDir, err)
|
|
}
|
|
|
|
// Step 3: Move into the kubo folder (cd kubo)
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Step 3: Running installation script...\n")
|
|
|
|
// Step 4: Run the installation script (sudo bash install.sh)
|
|
installScript := filepath.Join(kuboDir, "install.sh")
|
|
if _, err := os.Stat(installScript); err != nil {
|
|
return fmt.Errorf("install.sh not found in extracted kubo directory at %s: %w", installScript, err)
|
|
}
|
|
|
|
cmd = exec.Command("bash", installScript)
|
|
cmd.Dir = kuboDir
|
|
if output, err := cmd.CombinedOutput(); err != nil {
|
|
return fmt.Errorf("failed to run install.sh: %v\n%s", err, string(output))
|
|
}
|
|
|
|
// Step 5: Test that Kubo has installed correctly
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Step 5: Verifying installation...\n")
|
|
cmd = exec.Command("ipfs", "--version")
|
|
output, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
// ipfs might not be in PATH yet in this process, check file directly
|
|
ipfsLocations := []string{"/usr/local/bin/ipfs", "/usr/bin/ipfs"}
|
|
found := false
|
|
for _, loc := range ipfsLocations {
|
|
if info, err := os.Stat(loc); err == nil && !info.IsDir() {
|
|
found = true
|
|
// Ensure it's executable
|
|
if info.Mode()&0111 == 0 {
|
|
os.Chmod(loc, 0755)
|
|
}
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
return fmt.Errorf("ipfs binary not found after installation in %v", ipfsLocations)
|
|
}
|
|
} else {
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " %s", string(output))
|
|
}
|
|
|
|
// Ensure PATH is updated for current process
|
|
os.Setenv("PATH", os.Getenv("PATH")+":/usr/local/bin")
|
|
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " ✓ IPFS installed successfully\n")
|
|
return nil
|
|
}
|
|
|
|
// InstallIPFSCluster downloads and installs IPFS Cluster Service
|
|
func (bi *BinaryInstaller) InstallIPFSCluster() error {
|
|
if _, err := exec.LookPath("ipfs-cluster-service"); err == nil {
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " ✓ IPFS Cluster already installed\n")
|
|
return nil
|
|
}
|
|
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Installing IPFS Cluster Service...\n")
|
|
|
|
// Check if Go is available
|
|
if _, err := exec.LookPath("go"); err != nil {
|
|
return fmt.Errorf("go not found - required to install IPFS Cluster. Please install Go first")
|
|
}
|
|
|
|
cmd := exec.Command("go", "install", "github.com/ipfs-cluster/ipfs-cluster/cmd/ipfs-cluster-service@latest")
|
|
cmd.Env = append(os.Environ(), "GOBIN=/usr/local/bin")
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("failed to install IPFS Cluster: %w", err)
|
|
}
|
|
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " ✓ IPFS Cluster installed\n")
|
|
return nil
|
|
}
|
|
|
|
// InstallOlric downloads and installs Olric server
|
|
func (bi *BinaryInstaller) InstallOlric() error {
|
|
if _, err := exec.LookPath("olric-server"); err == nil {
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " ✓ Olric already installed\n")
|
|
return nil
|
|
}
|
|
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Installing Olric...\n")
|
|
|
|
// Check if Go is available
|
|
if _, err := exec.LookPath("go"); err != nil {
|
|
return fmt.Errorf("go not found - required to install Olric. Please install Go first")
|
|
}
|
|
|
|
cmd := exec.Command("go", "install", "github.com/olric-data/olric/cmd/olric-server@v0.7.0")
|
|
cmd.Env = append(os.Environ(), "GOBIN=/usr/local/bin")
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("failed to install Olric: %w", err)
|
|
}
|
|
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " ✓ Olric installed\n")
|
|
return nil
|
|
}
|
|
|
|
// InstallGo downloads and installs Go toolchain
|
|
func (bi *BinaryInstaller) InstallGo() error {
|
|
if _, err := exec.LookPath("go"); err == nil {
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " ✓ Go already installed\n")
|
|
return nil
|
|
}
|
|
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Installing Go...\n")
|
|
|
|
goTarball := fmt.Sprintf("go1.21.6.linux-%s.tar.gz", bi.arch)
|
|
goURL := fmt.Sprintf("https://go.dev/dl/%s", goTarball)
|
|
|
|
// Download
|
|
cmd := exec.Command("wget", "-q", goURL, "-O", "/tmp/"+goTarball)
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("failed to download Go: %w", err)
|
|
}
|
|
|
|
// Extract
|
|
cmd = exec.Command("tar", "-C", "/usr/local", "-xzf", "/tmp/"+goTarball)
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("failed to extract Go: %w", err)
|
|
}
|
|
|
|
// Add to PATH
|
|
newPath := os.Getenv("PATH") + ":/usr/local/go/bin"
|
|
os.Setenv("PATH", newPath)
|
|
|
|
// Verify installation
|
|
if _, err := exec.LookPath("go"); err != nil {
|
|
return fmt.Errorf("go installed but not found in PATH after installation")
|
|
}
|
|
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " ✓ Go installed\n")
|
|
return nil
|
|
}
|
|
|
|
// ResolveBinaryPath finds the fully-qualified path to a required executable
|
|
func (bi *BinaryInstaller) ResolveBinaryPath(binary string, extraPaths ...string) (string, error) {
|
|
// First try to find in PATH
|
|
if path, err := exec.LookPath(binary); err == nil {
|
|
if abs, err := filepath.Abs(path); err == nil {
|
|
return abs, nil
|
|
}
|
|
return path, nil
|
|
}
|
|
|
|
// Then try extra candidate paths
|
|
for _, candidate := range extraPaths {
|
|
if candidate == "" {
|
|
continue
|
|
}
|
|
if info, err := os.Stat(candidate); err == nil && !info.IsDir() && info.Mode()&0111 != 0 {
|
|
if abs, err := filepath.Abs(candidate); err == nil {
|
|
return abs, nil
|
|
}
|
|
return candidate, nil
|
|
}
|
|
}
|
|
|
|
// Not found - generate error message
|
|
checked := make([]string, 0, len(extraPaths))
|
|
for _, candidate := range extraPaths {
|
|
if candidate != "" {
|
|
checked = append(checked, candidate)
|
|
}
|
|
}
|
|
|
|
if len(checked) == 0 {
|
|
return "", fmt.Errorf("required binary %q not found in path", binary)
|
|
}
|
|
|
|
return "", fmt.Errorf("required binary %q not found in path (also checked %s)", binary, strings.Join(checked, ", "))
|
|
}
|
|
|
|
// InstallDeBrosBinaries clones and builds DeBros binaries
|
|
func (bi *BinaryInstaller) InstallDeBrosBinaries(branch string, debrosHome string) error {
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Building DeBros binaries...\n")
|
|
|
|
srcDir := filepath.Join(debrosHome, "src")
|
|
binDir := filepath.Join(debrosHome, "bin")
|
|
|
|
// Ensure directories exist
|
|
os.MkdirAll(srcDir, 0755)
|
|
os.MkdirAll(binDir, 0755)
|
|
|
|
// Check if git repository is already initialized
|
|
repoInitialized := false
|
|
if _, err := os.Stat(filepath.Join(srcDir, ".git")); err == nil {
|
|
repoInitialized = true
|
|
}
|
|
|
|
// Clone repository if not present, otherwise update it
|
|
if !repoInitialized {
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Cloning repository...\n")
|
|
cmd := exec.Command("git", "clone", "--branch", branch, "--depth", "1", "https://github.com/DeBrosOfficial/network.git", srcDir)
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("failed to clone repository: %w", err)
|
|
}
|
|
} else {
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Updating repository to latest changes...\n")
|
|
if output, err := exec.Command("git", "-C", srcDir, "fetch", "origin", branch).CombinedOutput(); err != nil {
|
|
return fmt.Errorf("failed to fetch repository updates: %v\n%s", err, string(output))
|
|
}
|
|
if output, err := exec.Command("git", "-C", srcDir, "reset", "--hard", "origin/"+branch).CombinedOutput(); err != nil {
|
|
return fmt.Errorf("failed to reset repository: %v\n%s", err, string(output))
|
|
}
|
|
if output, err := exec.Command("git", "-C", srcDir, "clean", "-fd").CombinedOutput(); err != nil {
|
|
return fmt.Errorf("failed to clean repository: %v\n%s", err, string(output))
|
|
}
|
|
}
|
|
|
|
// Build binaries
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Building binaries...\n")
|
|
cmd := exec.Command("make", "build")
|
|
cmd.Dir = srcDir
|
|
cmd.Env = append(os.Environ(), "HOME="+debrosHome, "PATH="+os.Getenv("PATH")+":/usr/local/go/bin")
|
|
if output, err := cmd.CombinedOutput(); err != nil {
|
|
return fmt.Errorf("failed to build: %v\n%s", err, string(output))
|
|
}
|
|
|
|
// Copy binaries
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Copying binaries...\n")
|
|
cmd = exec.Command("sh", "-c", fmt.Sprintf("cp -r %s/bin/* %s/", srcDir, binDir))
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("failed to copy binaries: %w", err)
|
|
}
|
|
|
|
exec.Command("chmod", "-R", "755", binDir).Run()
|
|
exec.Command("chown", "-R", "debros:debros", binDir).Run()
|
|
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " ✓ DeBros binaries installed\n")
|
|
return nil
|
|
}
|
|
|
|
// InstallSystemDependencies installs system-level dependencies via apt
|
|
func (bi *BinaryInstaller) InstallSystemDependencies() error {
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Installing system dependencies...\n")
|
|
|
|
// Update package list
|
|
cmd := exec.Command("apt-get", "update")
|
|
if err := cmd.Run(); err != nil {
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Warning: apt update failed\n")
|
|
}
|
|
|
|
// Install dependencies
|
|
cmd = exec.Command("apt-get", "install", "-y", "curl", "git", "make", "build-essential", "wget")
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("failed to install dependencies: %w", err)
|
|
}
|
|
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " ✓ System dependencies installed\n")
|
|
return nil
|
|
}
|
|
|
|
// 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 {
|
|
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)
|
|
}
|
|
|
|
if err := os.MkdirAll(ipfsRepoPath, 0755); err != nil {
|
|
return fmt.Errorf("failed to create IPFS repo directory: %w", err)
|
|
}
|
|
|
|
// Resolve IPFS binary path
|
|
ipfsBinary, err := bi.ResolveBinaryPath("ipfs", "/usr/local/bin/ipfs", "/usr/bin/ipfs")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 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 {
|
|
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
|
|
}
|
|
|
|
// 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 {
|
|
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))
|
|
}
|
|
|
|
// Clear AutoConf placeholders from config to prevent Kubo startup errors
|
|
// When AutoConf is disabled, 'auto' placeholders must be replaced with explicit values or empty
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Clearing AutoConf placeholders from IPFS config...\n")
|
|
|
|
type configCommand struct {
|
|
desc string
|
|
args []string
|
|
}
|
|
|
|
// List of config replacements to clear 'auto' placeholders
|
|
cleanup := []configCommand{
|
|
{"clearing Bootstrap peers", []string{"config", "Bootstrap", "--json", "[]"}},
|
|
{"clearing Routing.DelegatedRouters", []string{"config", "Routing.DelegatedRouters", "--json", "[]"}},
|
|
{"clearing Ipns.DelegatedPublishers", []string{"config", "Ipns.DelegatedPublishers", "--json", "[]"}},
|
|
{"clearing DNS.Resolvers", []string{"config", "DNS.Resolvers", "--json", "{}"}},
|
|
}
|
|
|
|
for _, step := range cleanup {
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " %s...\n", step.desc)
|
|
cmd := exec.Command(ipfsBinary, step.args...)
|
|
cmd.Env = append(os.Environ(), "IPFS_PATH="+ipfsRepoPath)
|
|
if output, err := cmd.CombinedOutput(); err != nil {
|
|
return fmt.Errorf("failed while %s: %v\n%s", step.desc, err, string(output))
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fix ownership
|
|
exec.Command("chown", "-R", "debros:debros", ipfsRepoPath).Run()
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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 {
|
|
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)
|
|
}
|
|
|
|
if err := os.MkdirAll(clusterPath, 0755); err != nil {
|
|
return fmt.Errorf("failed to create IPFS Cluster directory: %w", err)
|
|
}
|
|
|
|
// Fix ownership before running init
|
|
exec.Command("chown", "-R", "debros:debros", clusterPath).Run()
|
|
|
|
// Resolve ipfs-cluster-service binary path
|
|
clusterBinary, err := bi.ResolveBinaryPath("ipfs-cluster-service", "/usr/local/bin/ipfs-cluster-service", "/usr/bin/ipfs-cluster-service")
|
|
if err != nil {
|
|
return fmt.Errorf("ipfs-cluster-service binary not found: %w", err)
|
|
}
|
|
|
|
// 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))
|
|
}
|
|
}
|
|
|
|
// 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 updates
|
|
exec.Command("chown", "-R", "debros:debros", clusterPath).Run()
|
|
|
|
return nil
|
|
}
|
|
|
|
// updateClusterSecret updates the secret field in IPFS Cluster service.json
|
|
func (bi *BinaryInstaller) updateClusterSecret(clusterPath, secret string) error {
|
|
serviceJSONPath := filepath.Join(clusterPath, "service.json")
|
|
|
|
// Read existing config
|
|
data, err := os.ReadFile(serviceJSONPath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read service.json: %w", err)
|
|
}
|
|
|
|
// Parse JSON
|
|
var config map[string]interface{}
|
|
if err := json.Unmarshal(data, &config); err != nil {
|
|
return fmt.Errorf("failed to parse service.json: %w", err)
|
|
}
|
|
|
|
// Update cluster secret
|
|
if cluster, ok := config["cluster"].(map[string]interface{}); ok {
|
|
cluster["secret"] = secret
|
|
} else {
|
|
config["cluster"] = map[string]interface{}{
|
|
"secret": secret,
|
|
}
|
|
}
|
|
|
|
// Write back
|
|
updatedData, err := json.MarshalIndent(config, "", " ")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal service.json: %w", err)
|
|
}
|
|
|
|
if err := os.WriteFile(serviceJSONPath, updatedData, 0644); err != nil {
|
|
return fmt.Errorf("failed to write service.json: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// InitializeRQLiteDataDir initializes RQLite data directory
|
|
func (bi *BinaryInstaller) InitializeRQLiteDataDir(nodeType, dataDir string) error {
|
|
fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Initializing RQLite data dir for %s...\n", nodeType)
|
|
|
|
if err := os.MkdirAll(dataDir, 0755); err != nil {
|
|
return fmt.Errorf("failed to create RQLite data directory: %w", err)
|
|
}
|
|
|
|
exec.Command("chown", "-R", "debros:debros", dataDir).Run()
|
|
return nil
|
|
}
|