diff --git a/Makefile b/Makefile index bd01b8d..1b7690f 100644 --- a/Makefile +++ b/Makefile @@ -94,6 +94,8 @@ build-linux: deps GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS_LINUX)" -trimpath -o bin-linux/orama-cli ./cmd/cli @echo "Building Olric for linux/amd64..." GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -trimpath -o bin-linux/olric-server github.com/olric-data/olric/cmd/olric-server + @echo "Building IPFS Cluster Service for linux/amd64..." + GOOS=linux GOARCH=amd64 GOBIN=$(CURDIR)/bin-linux go install -ldflags "-s -w" -trimpath github.com/ipfs-cluster/ipfs-cluster/cmd/ipfs-cluster-service@latest @echo "✓ All Linux binaries built in bin-linux/" @echo "" @echo "Next steps:" diff --git a/pkg/cli/production/install/command.go b/pkg/cli/production/install/command.go index d0de911..e61574a 100644 --- a/pkg/cli/production/install/command.go +++ b/pkg/cli/production/install/command.go @@ -14,7 +14,26 @@ func Handle(args []string) { os.Exit(1) } - // Create orchestrator + // Resolve base domain interactively if not provided (before local/VPS branch) + if flags.BaseDomain == "" { + flags.BaseDomain = promptForBaseDomain() + } + + // Local mode: not running as root → orchestrate install via SSH + if os.Geteuid() != 0 { + remote, err := NewRemoteOrchestrator(flags) + if err != nil { + fmt.Fprintf(os.Stderr, "❌ %v\n", err) + os.Exit(1) + } + if err := remote.Execute(); err != nil { + fmt.Fprintf(os.Stderr, "❌ %v\n", err) + os.Exit(1) + } + return + } + + // VPS mode: running as root on the VPS — existing behavior orchestrator, err := NewOrchestrator(flags) if err != nil { fmt.Fprintf(os.Stderr, "❌ %v\n", err) @@ -27,12 +46,6 @@ func Handle(args []string) { os.Exit(1) } - // Check root privileges - if err := orchestrator.validator.ValidateRootPrivileges(); err != nil { - fmt.Fprintf(os.Stderr, "❌ %v\n", err) - os.Exit(1) - } - // Check port availability before proceeding if err := orchestrator.validator.ValidatePorts(); err != nil { fmt.Fprintf(os.Stderr, "❌ %v\n", err) diff --git a/pkg/cli/production/install/flags.go b/pkg/cli/production/install/flags.go index 66d9738..5b55e64 100644 --- a/pkg/cli/production/install/flags.go +++ b/pkg/cli/production/install/flags.go @@ -8,15 +8,12 @@ import ( // Flags represents install command flags type Flags struct { - VpsIP string - Domain string - BaseDomain string // Base domain for deployment routing (e.g., "dbrs.space") - Branch string - NoPull bool - Force bool - DryRun bool - SkipChecks bool - PreBuilt bool // Skip building binaries, use pre-built binaries already on disk + VpsIP string + Domain string + BaseDomain string // Base domain for deployment routing (e.g., "dbrs.space") + Force bool + DryRun bool + SkipChecks bool Nameserver bool // Make this node a nameserver (runs CoreDNS + Caddy) JoinAddress string // HTTPS URL of existing node (e.g., https://node1.dbrs.space) Token string // Invite token for joining (from orama invite) @@ -57,12 +54,9 @@ func ParseFlags(args []string) (*Flags, error) { fs.StringVar(&flags.VpsIP, "vps-ip", "", "Public IP of this VPS (required)") fs.StringVar(&flags.Domain, "domain", "", "Domain name for HTTPS (optional, e.g. gateway.example.com)") fs.StringVar(&flags.BaseDomain, "base-domain", "", "Base domain for deployment routing (e.g., dbrs.space)") - fs.StringVar(&flags.Branch, "branch", "main", "Git branch to use (main or nightly)") - fs.BoolVar(&flags.NoPull, "no-pull", false, "Skip git clone/pull, use existing repository in /home/debros/src") fs.BoolVar(&flags.Force, "force", false, "Force reconfiguration even if already installed") fs.BoolVar(&flags.DryRun, "dry-run", false, "Show what would be done without making changes") fs.BoolVar(&flags.SkipChecks, "skip-checks", false, "Skip minimum resource checks (RAM/CPU)") - fs.BoolVar(&flags.PreBuilt, "pre-built", false, "Skip building binaries on VPS, use pre-built binaries already in /home/debros/bin and /usr/local/bin") fs.BoolVar(&flags.Nameserver, "nameserver", false, "Make this node a nameserver (runs CoreDNS + Caddy)") // Cluster join flags diff --git a/pkg/cli/production/install/orchestrator.go b/pkg/cli/production/install/orchestrator.go index 2a3d01d..618bf2c 100644 --- a/pkg/cli/production/install/orchestrator.go +++ b/pkg/cli/production/install/orchestrator.go @@ -33,18 +33,13 @@ func NewOrchestrator(flags *Flags) (*Orchestrator, error) { oramaHome := "/home/debros" oramaDir := oramaHome + "/.orama" - // Prompt for base domain if not provided via flag - if flags.BaseDomain == "" { - flags.BaseDomain = promptForBaseDomain() - } - // Normalize peers peers, err := utils.NormalizePeers(flags.PeersStr) if err != nil { return nil, fmt.Errorf("invalid peers: %w", err) } - setup := production.NewProductionSetup(oramaHome, os.Stdout, flags.Force, flags.Branch, flags.NoPull, flags.SkipChecks, flags.PreBuilt) + setup := production.NewProductionSetup(oramaHome, os.Stdout, flags.Force, flags.SkipChecks) setup.SetNameserver(flags.Nameserver) // Configure Anyone mode @@ -84,18 +79,6 @@ func NewOrchestrator(flags *Flags) (*Orchestrator, error) { func (o *Orchestrator) Execute() error { fmt.Printf("🚀 Starting production installation...\n\n") - // Inform user if skipping git pull - if o.flags.NoPull { - fmt.Printf(" ⚠️ --no-pull flag enabled: Skipping git clone/pull\n") - fmt.Printf(" Using existing repository at /home/debros/src\n") - } - - // Inform user if using pre-built binaries - if o.flags.PreBuilt { - fmt.Printf(" ⚠️ --pre-built flag enabled: Skipping all Go compilation\n") - fmt.Printf(" Using pre-built binaries from /home/debros/bin and /usr/local/bin\n") - } - // Validate DNS if domain is provided o.validator.ValidateDNS() @@ -112,7 +95,7 @@ func (o *Orchestrator) Execute() error { ORPort: o.flags.AnyoneORPort, } } - utils.ShowDryRunSummaryWithRelay(o.flags.VpsIP, o.flags.Domain, o.flags.Branch, o.peers, o.flags.JoinAddress, o.validator.IsFirstNode(), o.oramaDir, relayInfo) + utils.ShowDryRunSummaryWithRelay(o.flags.VpsIP, o.flags.Domain, "main", o.peers, o.flags.JoinAddress, o.validator.IsFirstNode(), o.oramaDir, relayInfo) return nil } @@ -131,7 +114,7 @@ func (o *Orchestrator) Execute() error { anyoneORPort = 9001 } prefs := &production.NodePreferences{ - Branch: o.flags.Branch, + Branch: "main", Nameserver: o.flags.Nameserver, AnyoneClient: o.flags.AnyoneClient, AnyoneRelay: o.flags.AnyoneRelay, diff --git a/pkg/cli/production/upgrade/flags.go b/pkg/cli/production/upgrade/flags.go index 193aa63..dc2006e 100644 --- a/pkg/cli/production/upgrade/flags.go +++ b/pkg/cli/production/upgrade/flags.go @@ -10,10 +10,7 @@ import ( type Flags struct { Force bool RestartServices bool - NoPull bool - PreBuilt bool SkipChecks bool - Branch string Nameserver *bool // Pointer so we can detect if explicitly set vs default // Anyone flags @@ -39,10 +36,7 @@ func ParseFlags(args []string) (*Flags, error) { fs.BoolVar(&flags.Force, "force", false, "Reconfigure all settings") fs.BoolVar(&flags.RestartServices, "restart", false, "Automatically restart services after upgrade") - fs.BoolVar(&flags.NoPull, "no-pull", false, "Skip source download, use existing /home/debros/src") - fs.BoolVar(&flags.PreBuilt, "pre-built", false, "Skip building binaries on VPS, use pre-built binaries already in /home/debros/bin and /usr/local/bin") fs.BoolVar(&flags.SkipChecks, "skip-checks", false, "Skip minimum resource checks (RAM/CPU)") - fs.StringVar(&flags.Branch, "branch", "", "Git branch to use (uses saved preference if not specified)") // Nameserver flag - use pointer to detect if explicitly set nameserver := fs.Bool("nameserver", false, "Make this node a nameserver (uses saved preference if not specified)") diff --git a/pkg/cli/production/upgrade/orchestrator.go b/pkg/cli/production/upgrade/orchestrator.go index 3268610..609433e 100644 --- a/pkg/cli/production/upgrade/orchestrator.go +++ b/pkg/cli/production/upgrade/orchestrator.go @@ -32,19 +32,13 @@ func NewOrchestrator(flags *Flags) *Orchestrator { // Load existing preferences prefs := production.LoadPreferences(oramaDir) - // Use saved branch if not specified - branch := flags.Branch - if branch == "" { - branch = prefs.Branch - } - // Use saved nameserver preference if not explicitly specified isNameserver := prefs.Nameserver if flags.Nameserver != nil { isNameserver = *flags.Nameserver } - setup := production.NewProductionSetup(oramaHome, os.Stdout, flags.Force, branch, flags.NoPull, flags.SkipChecks, flags.PreBuilt) + setup := production.NewProductionSetup(oramaHome, os.Stdout, flags.Force, flags.SkipChecks) setup.SetNameserver(isNameserver) // Configure Anyone mode (flag > saved preference > auto-detect) @@ -103,18 +97,6 @@ func (o *Orchestrator) Execute() error { fmt.Printf(" This will preserve existing configurations and data\n") fmt.Printf(" Configurations will be updated to latest format\n\n") - // Log if --no-pull is enabled - if o.flags.NoPull { - fmt.Printf(" ⚠️ --no-pull flag enabled: Skipping git clone/pull\n") - fmt.Printf(" Using existing repository at %s/src\n", o.oramaHome) - } - - // Log if --pre-built is enabled - if o.flags.PreBuilt { - fmt.Printf(" ⚠️ --pre-built flag enabled: Skipping all Go compilation\n") - fmt.Printf(" Using pre-built binaries from %s/bin and /usr/local/bin\n", o.oramaHome) - } - // Handle branch preferences if err := o.handleBranchPreferences(); err != nil { return err @@ -216,15 +198,6 @@ func (o *Orchestrator) handleBranchPreferences() error { prefs := production.LoadPreferences(o.oramaDir) prefsChanged := false - // If branch was explicitly provided, update it - if o.flags.Branch != "" { - prefs.Branch = o.flags.Branch - prefsChanged = true - fmt.Printf(" Using branch: %s (saved for future upgrades)\n", o.flags.Branch) - } else { - fmt.Printf(" Using branch: %s (from saved preference)\n", prefs.Branch) - } - // If nameserver was explicitly provided, update it if o.flags.Nameserver != nil { prefs.Nameserver = *o.flags.Nameserver diff --git a/pkg/environments/production/checks.go b/pkg/environments/production/checks.go index e8e3b45..39dd58b 100644 --- a/pkg/environments/production/checks.go +++ b/pkg/environments/production/checks.go @@ -116,66 +116,84 @@ func (ad *ArchitectureDetector) Detect() (string, error) { } } -// DependencyChecker validates external tool availability -type DependencyChecker struct { - skipOptional bool -} +// DependencyChecker validates external tool availability and auto-installs missing ones +type DependencyChecker struct{} // NewDependencyChecker creates a new checker -func NewDependencyChecker(skipOptional bool) *DependencyChecker { - return &DependencyChecker{ - skipOptional: skipOptional, - } +func NewDependencyChecker(_ bool) *DependencyChecker { + return &DependencyChecker{} } // Dependency represents an external binary dependency type Dependency struct { - Name string - Command string - Optional bool - InstallHint string + Name string + Command string + AptPkg string // apt package name to install } -// CheckAll validates all required dependencies +// CheckAll validates all required dependencies, auto-installing any that are missing. func (dc *DependencyChecker) CheckAll() ([]Dependency, error) { dependencies := []Dependency{ - { - Name: "curl", - Command: "curl", - Optional: false, - InstallHint: "Usually pre-installed; if missing: apt-get install curl", - }, - { - Name: "git", - Command: "git", - Optional: false, - InstallHint: "Install with: apt-get install git", - }, - { - Name: "make", - Command: "make", - Optional: false, - InstallHint: "Install with: apt-get install make", - }, + {Name: "curl", Command: "curl", AptPkg: "curl"}, + {Name: "git", Command: "git", AptPkg: "git"}, + {Name: "make", Command: "make", AptPkg: "make"}, + {Name: "jq", Command: "jq", AptPkg: "jq"}, + {Name: "speedtest", Command: "speedtest-cli", AptPkg: "speedtest-cli"}, } var missing []Dependency for _, dep := range dependencies { if _, err := exec.LookPath(dep.Command); err != nil { - if !dep.Optional || !dc.skipOptional { - missing = append(missing, dep) - } + missing = append(missing, dep) } } - if len(missing) > 0 { - errMsg := "missing required dependencies:\n" - for _, dep := range missing { - errMsg += fmt.Sprintf(" - %s (%s): %s\n", dep.Name, dep.Command, dep.InstallHint) - } - return missing, fmt.Errorf("%s", errMsg) + if len(missing) == 0 { + return nil, nil } + // Auto-install missing dependencies + var pkgs []string + var names []string + for _, dep := range missing { + pkgs = append(pkgs, dep.AptPkg) + names = append(names, dep.Name) + } + + fmt.Fprintf(os.Stderr, " Installing missing dependencies: %s\n", strings.Join(names, ", ")) + + // apt-get update first + update := exec.Command("apt-get", "update", "-qq") + update.Stdout = os.Stdout + update.Stderr = os.Stderr + update.Run() // best-effort, don't fail on update + + // apt-get install + args := append([]string{"install", "-y", "-qq"}, pkgs...) + install := exec.Command("apt-get", args...) + install.Stdout = os.Stdout + install.Stderr = os.Stderr + if err := install.Run(); err != nil { + return missing, fmt.Errorf("failed to install dependencies (%s): %w", strings.Join(names, ", "), err) + } + + // Verify after install + var stillMissing []Dependency + for _, dep := range missing { + if _, err := exec.LookPath(dep.Command); err != nil { + stillMissing = append(stillMissing, dep) + } + } + + if len(stillMissing) > 0 { + errMsg := "dependencies still missing after install attempt:\n" + for _, dep := range stillMissing { + errMsg += fmt.Sprintf(" - %s\n", dep.Name) + } + return stillMissing, fmt.Errorf("%s", errMsg) + } + + fmt.Fprintf(os.Stderr, " ✓ Dependencies installed successfully\n") return nil, nil } diff --git a/pkg/environments/production/installers.go b/pkg/environments/production/installers.go index 67955d7..3fbc295 100644 --- a/pkg/environments/production/installers.go +++ b/pkg/environments/production/installers.go @@ -72,9 +72,9 @@ func (bi *BinaryInstaller) ResolveBinaryPath(binary string, extraPaths ...string return installers.ResolveBinaryPath(binary, extraPaths...) } -// InstallDeBrosBinaries clones and builds DeBros binaries -func (bi *BinaryInstaller) InstallDeBrosBinaries(branch string, oramaHome string, skipRepoUpdate bool) error { - return bi.gateway.InstallDeBrosBinaries(branch, oramaHome, skipRepoUpdate) +// InstallDeBrosBinaries builds DeBros binaries from source +func (bi *BinaryInstaller) InstallDeBrosBinaries(oramaHome string) error { + return bi.gateway.InstallDeBrosBinaries(oramaHome) } // InstallSystemDependencies installs system-level dependencies via apt diff --git a/pkg/environments/production/installers/gateway.go b/pkg/environments/production/installers/gateway.go index 6322d9b..e2e0d34 100644 --- a/pkg/environments/production/installers/gateway.go +++ b/pkg/environments/production/installers/gateway.go @@ -39,78 +39,9 @@ func (gi *GatewayInstaller) Configure() error { return nil } -// downloadSourceZIP downloads source code as ZIP from GitHub -// This is simpler and more reliable than git clone with shallow clones -func (gi *GatewayInstaller) downloadSourceZIP(branch string, srcDir string) error { - // GitHub archive URL format - zipURL := fmt.Sprintf("https://github.com/DeBrosOfficial/network/archive/refs/heads/%s.zip", branch) - zipPath := "/tmp/network-source.zip" - extractDir := "/tmp/network-extract" - - // Clean up any previous download artifacts - os.RemoveAll(zipPath) - os.RemoveAll(extractDir) - - // Download ZIP - fmt.Fprintf(gi.logWriter, " Downloading source (branch: %s)...\n", branch) - if err := DownloadFile(zipURL, zipPath); err != nil { - return fmt.Errorf("failed to download source from %s: %w", zipURL, err) - } - - // Create extraction directory - if err := os.MkdirAll(extractDir, 0755); err != nil { - return fmt.Errorf("failed to create extraction directory: %w", err) - } - - // Extract ZIP - fmt.Fprintf(gi.logWriter, " Extracting source...\n") - extractCmd := exec.Command("unzip", "-q", "-o", zipPath, "-d", extractDir) - if output, err := extractCmd.CombinedOutput(); err != nil { - return fmt.Errorf("failed to extract source: %w\n%s", err, string(output)) - } - - // GitHub extracts to network-{branch}/ directory - extractedDir := filepath.Join(extractDir, fmt.Sprintf("network-%s", branch)) - - // Verify extracted directory exists - if _, err := os.Stat(extractedDir); os.IsNotExist(err) { - // Try alternative naming (GitHub may sanitize branch names) - entries, _ := os.ReadDir(extractDir) - if len(entries) == 1 && entries[0].IsDir() { - extractedDir = filepath.Join(extractDir, entries[0].Name()) - } else { - return fmt.Errorf("extracted directory not found at %s", extractedDir) - } - } - - // Remove existing source directory - os.RemoveAll(srcDir) - - // Move extracted content to source directory - if err := os.Rename(extractedDir, srcDir); err != nil { - // Cross-filesystem fallback: copy instead of rename - fmt.Fprintf(gi.logWriter, " Moving source (cross-filesystem copy)...\n") - copyCmd := exec.Command("cp", "-r", extractedDir, srcDir) - if output, err := copyCmd.CombinedOutput(); err != nil { - return fmt.Errorf("failed to move source: %w\n%s", err, string(output)) - } - } - - // Cleanup temp files - os.RemoveAll(zipPath) - os.RemoveAll(extractDir) - - // Fix ownership - if err := exec.Command("chown", "-R", "debros:debros", srcDir).Run(); err != nil { - fmt.Fprintf(gi.logWriter, " ⚠️ Warning: failed to chown source directory: %v\n", err) - } - - fmt.Fprintf(gi.logWriter, " ✓ Source downloaded\n") - return nil -} - -// InstallDeBrosBinaries downloads and builds DeBros binaries -func (gi *GatewayInstaller) InstallDeBrosBinaries(branch string, oramaHome string, skipRepoUpdate bool) error { +// InstallDeBrosBinaries builds DeBros binaries from source at /home/debros/src. +// Source must already be present (uploaded via SCP archive). +func (gi *GatewayInstaller) InstallDeBrosBinaries(oramaHome string) error { fmt.Fprintf(gi.logWriter, " Building DeBros binaries...\n") srcDir := filepath.Join(oramaHome, "src") @@ -124,24 +55,9 @@ func (gi *GatewayInstaller) InstallDeBrosBinaries(branch string, oramaHome strin return fmt.Errorf("failed to create bin directory %s: %w", binDir, err) } - // Check if source directory has content - hasSourceContent := false - if entries, err := os.ReadDir(srcDir); err == nil && len(entries) > 0 { - hasSourceContent = true - } - - // Handle repository update/download based on skipRepoUpdate flag - if skipRepoUpdate { - fmt.Fprintf(gi.logWriter, " Skipping source download (--no-pull flag)\n") - if !hasSourceContent { - return fmt.Errorf("cannot skip download: source directory is empty at %s (need to populate it first)", srcDir) - } - fmt.Fprintf(gi.logWriter, " Using existing source at %s\n", srcDir) - } else { - // Download source as ZIP from GitHub (simpler than git, no shallow clone issues) - if err := gi.downloadSourceZIP(branch, srcDir); err != nil { - return err - } + // Verify source exists + if entries, err := os.ReadDir(srcDir); err != nil || len(entries) == 0 { + return fmt.Errorf("source directory is empty at %s (upload source archive first)", srcDir) } // Build binaries diff --git a/pkg/environments/production/orchestrator.go b/pkg/environments/production/orchestrator.go index fa3492f..374e178 100644 --- a/pkg/environments/production/orchestrator.go +++ b/pkg/environments/production/orchestrator.go @@ -52,9 +52,6 @@ type ProductionSetup struct { serviceGenerator *SystemdServiceGenerator serviceController *SystemdController binaryInstaller *BinaryInstaller - branch string - skipRepoUpdate bool - skipBuild bool // Skip all Go compilation (use pre-built binaries) NodePeerID string // Captured during Phase3 for later display } @@ -86,24 +83,16 @@ func SaveBranchPreference(oramaDir, branch string) error { } // NewProductionSetup creates a new production setup orchestrator -func NewProductionSetup(oramaHome string, logWriter io.Writer, forceReconfigure bool, branch string, skipRepoUpdate bool, skipResourceChecks bool, skipBuild bool) *ProductionSetup { +func NewProductionSetup(oramaHome string, logWriter io.Writer, forceReconfigure bool, skipResourceChecks bool) *ProductionSetup { oramaDir := filepath.Join(oramaHome, ".orama") arch, _ := (&ArchitectureDetector{}).Detect() - // If branch is empty, try to read from stored preference, otherwise default to main - if branch == "" { - branch = ReadBranchPreference(oramaDir) - } - return &ProductionSetup{ oramaHome: oramaHome, oramaDir: oramaDir, logWriter: logWriter, forceReconfigure: forceReconfigure, arch: arch, - branch: branch, - skipRepoUpdate: skipRepoUpdate, - skipBuild: skipBuild, skipResourceChecks: skipResourceChecks, privChecker: &PrivilegeChecker{}, osDetector: &OSDetector{}, @@ -201,12 +190,12 @@ func (ps *ProductionSetup) Phase1CheckPrerequisites() error { ps.arch = arch ps.logf(" ✓ Detected architecture: %s", arch) - // Check basic dependencies + // Check basic dependencies (auto-installs missing ones) depChecker := NewDependencyChecker(ps.skipOptionalDeps) if missing, err := depChecker.CheckAll(); err != nil { - ps.logf(" ❌ Missing dependencies:") + ps.logf(" ❌ Failed to install dependencies:") for _, dep := range missing { - ps.logf(" - %s: %s", dep.Name, dep.InstallHint) + ps.logf(" - %s", dep.Name) } return err } @@ -307,86 +296,30 @@ func (ps *ProductionSetup) Phase2bInstallBinaries() error { ps.logf(" ⚠️ System dependencies warning: %v", err) } - if ps.skipBuild { - // --pre-built mode: skip all Go compilation, verify binaries exist - ps.logf(" ℹ️ --pre-built mode: skipping Go installation and all compilation") + // Install Go toolchain (downloads from go.dev if needed) + if err := ps.binaryInstaller.InstallGo(); err != nil { + return fmt.Errorf("failed to install Go: %w", err) + } - // Verify required DeBros binaries exist - binDir := filepath.Join(ps.oramaHome, "bin") - requiredBins := []string{"orama-node", "gateway", "orama", "identity"} - for _, bin := range requiredBins { - binPath := filepath.Join(binDir, bin) - if _, err := os.Stat(binPath); os.IsNotExist(err) { - return fmt.Errorf("--pre-built: required binary not found at %s (run 'make build-linux' locally and copy to VPS)", binPath) - } - ps.logf(" ✓ Found %s", binPath) + if err := ps.binaryInstaller.InstallOlric(); err != nil { + ps.logf(" ⚠️ Olric install warning: %v", err) + } + + // Install DeBros binaries (source must be at /home/debros/src via SCP) + if err := ps.binaryInstaller.InstallDeBrosBinaries(ps.oramaHome); err != nil { + return fmt.Errorf("failed to install DeBros binaries: %w", err) + } + + // Install CoreDNS only for nameserver nodes + if ps.isNameserver { + if err := ps.binaryInstaller.InstallCoreDNS(); err != nil { + ps.logf(" ⚠️ CoreDNS install warning: %v", err) } + } - // Grant CAP_NET_BIND_SERVICE to orama-node - nodeBinary := filepath.Join(binDir, "orama-node") - if err := exec.Command("setcap", "cap_net_bind_service=+ep", nodeBinary).Run(); err != nil { - ps.logf(" ⚠️ Warning: failed to setcap on orama-node: %v", err) - } - - // Verify Olric - if _, err := exec.LookPath("olric-server"); err != nil { - // Check if it's in the bin dir - olricPath := filepath.Join(binDir, "olric-server") - if _, err := os.Stat(olricPath); os.IsNotExist(err) { - return fmt.Errorf("--pre-built: olric-server not found in PATH or %s", binDir) - } - // Copy to /usr/local/bin - if data, err := os.ReadFile(olricPath); err == nil { - os.WriteFile("/usr/local/bin/olric-server", data, 0755) - } - ps.logf(" ✓ Found %s", olricPath) - } else { - ps.logf(" ✓ olric-server already in PATH") - } - - // Verify CoreDNS and Caddy if nameserver - if ps.isNameserver { - if _, err := os.Stat("/usr/local/bin/coredns"); os.IsNotExist(err) { - return fmt.Errorf("--pre-built: coredns not found at /usr/local/bin/coredns") - } - ps.logf(" ✓ Found /usr/local/bin/coredns") - - if _, err := os.Stat("/usr/bin/caddy"); os.IsNotExist(err) { - return fmt.Errorf("--pre-built: caddy not found at /usr/bin/caddy") - } - ps.logf(" ✓ Found /usr/bin/caddy") - - // Grant CAP_NET_BIND_SERVICE to caddy - if err := exec.Command("setcap", "cap_net_bind_service=+ep", "/usr/bin/caddy").Run(); err != nil { - ps.logf(" ⚠️ Warning: failed to setcap on caddy: %v", err) - } - } - } else { - // Normal mode: install Go and build everything - if err := ps.binaryInstaller.InstallGo(); err != nil { - return fmt.Errorf("failed to install Go: %w", err) - } - - if err := ps.binaryInstaller.InstallOlric(); err != nil { - ps.logf(" ⚠️ Olric install warning: %v", err) - } - - // Install DeBros binaries (must be done before CoreDNS since we need the RQLite plugin source) - if err := ps.binaryInstaller.InstallDeBrosBinaries(ps.branch, ps.oramaHome, ps.skipRepoUpdate); err != nil { - return fmt.Errorf("failed to install DeBros binaries: %w", err) - } - - // Install CoreDNS only for nameserver nodes - if ps.isNameserver { - if err := ps.binaryInstaller.InstallCoreDNS(); err != nil { - ps.logf(" ⚠️ CoreDNS install warning: %v", err) - } - } - - // Install Caddy on ALL nodes (any node may host namespaces and need TLS) - if err := ps.binaryInstaller.InstallCaddy(); err != nil { - ps.logf(" ⚠️ Caddy install warning: %v", err) - } + // Install Caddy on ALL nodes (any node may host namespaces and need TLS) + if err := ps.binaryInstaller.InstallCaddy(); err != nil { + ps.logf(" ⚠️ Caddy install warning: %v", err) } // These are pre-built binary downloads (not Go compilation), always run them