mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-03-17 05:13:01 +00:00
Deployement updates
This commit is contained in:
parent
b5109f1ee8
commit
9282fe64ee
@ -17,14 +17,34 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNamespaceIsolation_Deployments(t *testing.T) {
|
||||
// Setup two test environments with different namespaces
|
||||
// TestNamespaceIsolation creates two namespaces once and runs all isolation
|
||||
// subtests against them. This keeps namespace usage to 2 regardless of how
|
||||
// many isolation scenarios we test.
|
||||
func TestNamespaceIsolation(t *testing.T) {
|
||||
envA, err := e2e.LoadTestEnvWithNamespace("namespace-a-" + fmt.Sprintf("%d", time.Now().Unix()))
|
||||
require.NoError(t, err, "Failed to create namespace A environment")
|
||||
|
||||
envB, err := e2e.LoadTestEnvWithNamespace("namespace-b-" + fmt.Sprintf("%d", time.Now().Unix()))
|
||||
require.NoError(t, err, "Failed to create namespace B environment")
|
||||
|
||||
t.Run("Deployments", func(t *testing.T) {
|
||||
testNamespaceIsolationDeployments(t, envA, envB)
|
||||
})
|
||||
|
||||
t.Run("SQLiteDatabases", func(t *testing.T) {
|
||||
testNamespaceIsolationSQLiteDatabases(t, envA, envB)
|
||||
})
|
||||
|
||||
t.Run("IPFSContent", func(t *testing.T) {
|
||||
testNamespaceIsolationIPFSContent(t, envA, envB)
|
||||
})
|
||||
|
||||
t.Run("OlricCache", func(t *testing.T) {
|
||||
testNamespaceIsolationOlricCache(t, envA, envB)
|
||||
})
|
||||
}
|
||||
|
||||
func testNamespaceIsolationDeployments(t *testing.T, envA, envB *e2e.E2ETestEnv) {
|
||||
tarballPath := filepath.Join("../../testdata/apps/react-app")
|
||||
|
||||
// Create deployment in namespace-a
|
||||
@ -112,13 +132,7 @@ func TestNamespaceIsolation_Deployments(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestNamespaceIsolation_SQLiteDatabases(t *testing.T) {
|
||||
envA, err := e2e.LoadTestEnvWithNamespace("namespace-a-" + fmt.Sprintf("%d", time.Now().Unix()))
|
||||
require.NoError(t, err, "Should create test environment for namespace-a")
|
||||
|
||||
envB, err := e2e.LoadTestEnvWithNamespace("namespace-b-" + fmt.Sprintf("%d", time.Now().Unix()))
|
||||
require.NoError(t, err, "Should create test environment for namespace-b")
|
||||
|
||||
func testNamespaceIsolationSQLiteDatabases(t *testing.T, envA, envB *e2e.E2ETestEnv) {
|
||||
// Create database in namespace-a
|
||||
dbNameA := "users-db-a"
|
||||
e2e.CreateSQLiteDB(t, envA, dbNameA)
|
||||
@ -201,13 +215,7 @@ func TestNamespaceIsolation_SQLiteDatabases(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestNamespaceIsolation_IPFSContent(t *testing.T) {
|
||||
envA, err := e2e.LoadTestEnvWithNamespace("namespace-a-" + fmt.Sprintf("%d", time.Now().Unix()))
|
||||
require.NoError(t, err, "Should create test environment for namespace-a")
|
||||
|
||||
envB, err := e2e.LoadTestEnvWithNamespace("namespace-b-" + fmt.Sprintf("%d", time.Now().Unix()))
|
||||
require.NoError(t, err, "Should create test environment for namespace-b")
|
||||
|
||||
func testNamespaceIsolationIPFSContent(t *testing.T, envA, envB *e2e.E2ETestEnv) {
|
||||
// Upload file in namespace-a
|
||||
cidA := e2e.UploadTestFile(t, envA, "test-file-a.txt", "Content from namespace A")
|
||||
defer func() {
|
||||
@ -217,8 +225,6 @@ func TestNamespaceIsolation_IPFSContent(t *testing.T) {
|
||||
}()
|
||||
|
||||
t.Run("Namespace-B cannot GET Namespace-A IPFS content", func(t *testing.T) {
|
||||
// This tests application-level access control
|
||||
// IPFS content is globally accessible by CID, but our handlers should enforce namespace
|
||||
req, _ := http.NewRequest("GET", envB.GatewayURL+"/v1/storage/get/"+cidA, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+envB.APIKey)
|
||||
|
||||
@ -226,7 +232,6 @@ func TestNamespaceIsolation_IPFSContent(t *testing.T) {
|
||||
require.NoError(t, err, "Should execute request")
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Should return 403 or 404 (namespace doesn't own this CID)
|
||||
assert.Contains(t, []int{http.StatusNotFound, http.StatusForbidden}, resp.StatusCode,
|
||||
"Should block cross-namespace IPFS GET")
|
||||
|
||||
@ -273,13 +278,7 @@ func TestNamespaceIsolation_IPFSContent(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestNamespaceIsolation_OlricCache(t *testing.T) {
|
||||
envA, err := e2e.LoadTestEnvWithNamespace("namespace-a-" + fmt.Sprintf("%d", time.Now().Unix()))
|
||||
require.NoError(t, err, "Should create test environment for namespace-a")
|
||||
|
||||
envB, err := e2e.LoadTestEnvWithNamespace("namespace-b-" + fmt.Sprintf("%d", time.Now().Unix()))
|
||||
require.NoError(t, err, "Should create test environment for namespace-b")
|
||||
|
||||
func testNamespaceIsolationOlricCache(t *testing.T, envA, envB *e2e.E2ETestEnv) {
|
||||
dmap := "test-cache"
|
||||
keyA := "user-session-123"
|
||||
valueA := `{"user_id": "alice", "token": "secret-token-a"}`
|
||||
@ -342,7 +341,6 @@ func TestNamespaceIsolation_OlricCache(t *testing.T) {
|
||||
require.NoError(t, err, "Should execute request")
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Should return 404 or success (key doesn't exist in their namespace)
|
||||
assert.Contains(t, []int{http.StatusOK, http.StatusNotFound}, resp.StatusCode)
|
||||
|
||||
// Verify key still exists for namespace-a
|
||||
|
||||
@ -201,18 +201,60 @@ func CollectPortsForServices(services []string, skipActive bool) ([]PortSpec, er
|
||||
return ports, nil
|
||||
}
|
||||
|
||||
// EnsurePortsAvailable checks if the specified ports are available
|
||||
// EnsurePortsAvailable checks if the specified ports are available.
|
||||
// If a port is in use, it identifies the process and gives actionable guidance.
|
||||
func EnsurePortsAvailable(action string, ports []PortSpec) error {
|
||||
var conflicts []string
|
||||
for _, spec := range ports {
|
||||
ln, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", spec.Port))
|
||||
if err != nil {
|
||||
if errors.Is(err, syscall.EADDRINUSE) || strings.Contains(err.Error(), "address already in use") {
|
||||
return fmt.Errorf("%s cannot continue: %s (port %d) is already in use", action, spec.Name, spec.Port)
|
||||
processInfo := identifyPortProcess(spec.Port)
|
||||
conflicts = append(conflicts, fmt.Sprintf(" - %s (port %d): %s", spec.Name, spec.Port, processInfo))
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("%s cannot continue: failed to inspect %s (port %d): %w", action, spec.Name, spec.Port, err)
|
||||
}
|
||||
_ = ln.Close()
|
||||
}
|
||||
if len(conflicts) > 0 {
|
||||
msg := fmt.Sprintf("%s cannot continue: the following ports are already in use:\n%s\n\n", action, strings.Join(conflicts, "\n"))
|
||||
msg += "Please stop the conflicting services before running this command.\n"
|
||||
msg += "Common fixes:\n"
|
||||
msg += " - Docker: sudo systemctl stop docker docker.socket\n"
|
||||
msg += " - Old IPFS: sudo systemctl stop ipfs\n"
|
||||
msg += " - systemd-resolved: already handled by installer (port 53)\n"
|
||||
msg += " - Other services: sudo kill <PID> or sudo systemctl stop <service>"
|
||||
return fmt.Errorf(msg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// identifyPortProcess uses ss/lsof to find what process is using a port
|
||||
func identifyPortProcess(port int) string {
|
||||
// Try ss first (available on most Linux)
|
||||
out, err := exec.Command("ss", "-tlnp", fmt.Sprintf("sport = :%d", port)).CombinedOutput()
|
||||
if err == nil {
|
||||
lines := strings.Split(strings.TrimSpace(string(out)), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "users:") {
|
||||
// Extract process info from ss output like: users:(("docker-proxy",pid=2049,fd=4))
|
||||
if idx := strings.Index(line, "users:"); idx != -1 {
|
||||
return strings.TrimSpace(line[idx:])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: try lsof
|
||||
out, err = exec.Command("lsof", "-i", fmt.Sprintf(":%d", port), "-sTCP:LISTEN", "-n", "-P").CombinedOutput()
|
||||
if err == nil {
|
||||
lines := strings.Split(strings.TrimSpace(string(out)), "\n")
|
||||
if len(lines) > 1 {
|
||||
return strings.TrimSpace(lines[1]) // first data line after header
|
||||
}
|
||||
}
|
||||
|
||||
return "unknown process"
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package production
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
|
||||
@ -117,8 +118,12 @@ func (bi *BinaryInstaller) InstallAnyoneClient() error {
|
||||
return bi.gateway.InstallAnyoneClient()
|
||||
}
|
||||
|
||||
// InstallCoreDNS builds and installs CoreDNS with the custom RQLite plugin
|
||||
// InstallCoreDNS builds and installs CoreDNS with the custom RQLite plugin.
|
||||
// Also disables systemd-resolved's stub listener so CoreDNS can bind to port 53.
|
||||
func (bi *BinaryInstaller) InstallCoreDNS() error {
|
||||
if err := bi.coredns.DisableResolvedStubListener(); err != nil {
|
||||
fmt.Fprintf(bi.logWriter, " ⚠️ Failed to disable systemd-resolved stub: %v\n", err)
|
||||
}
|
||||
return bi.coredns.Install()
|
||||
}
|
||||
|
||||
|
||||
@ -171,8 +171,17 @@ func (ari *AnyoneRelayInstaller) Install() error {
|
||||
return fmt.Errorf("failed to add Anyone repository: %w", err)
|
||||
}
|
||||
|
||||
// Install the anon package
|
||||
cmd := exec.Command("apt-get", "install", "-y", "anon")
|
||||
// Pre-accept terms via debconf to avoid interactive prompt during apt install.
|
||||
// The anon package preinst script checks "anon/terms" via debconf.
|
||||
preseed := exec.Command("bash", "-c", `echo "anon anon/terms boolean true" | debconf-set-selections`)
|
||||
if output, err := preseed.CombinedOutput(); err != nil {
|
||||
fmt.Fprintf(ari.logWriter, " ⚠️ debconf preseed warning: %v (%s)\n", err, string(output))
|
||||
}
|
||||
|
||||
// Install the anon package non-interactively.
|
||||
// --force-confold keeps existing config files if present (e.g. during migration).
|
||||
cmd := exec.Command("apt-get", "install", "-y", "-o", "Dpkg::Options::=--force-confold", "anon")
|
||||
cmd.Env = append(os.Environ(), "DEBIAN_FRONTEND=noninteractive")
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("failed to install anon package: %w\n%s", err, string(output))
|
||||
}
|
||||
@ -180,6 +189,11 @@ func (ari *AnyoneRelayInstaller) Install() error {
|
||||
// Clean up
|
||||
os.Remove(installScript)
|
||||
|
||||
// Stop and disable the default 'anon' systemd service that the apt package
|
||||
// auto-enables. We use our own 'debros-anyone-relay' service instead.
|
||||
exec.Command("systemctl", "stop", "anon").Run()
|
||||
exec.Command("systemctl", "disable", "anon").Run()
|
||||
|
||||
fmt.Fprintf(ari.logWriter, " ✓ Anyone relay binary installed\n")
|
||||
|
||||
// Install nyx for relay monitoring (connects to ControlPort 9051)
|
||||
|
||||
@ -54,6 +54,46 @@ func (ci *CoreDNSInstaller) IsInstalled() bool {
|
||||
}
|
||||
|
||||
// Install builds and installs CoreDNS with the custom RQLite plugin
|
||||
// DisableResolvedStubListener disables systemd-resolved's DNS stub listener
|
||||
// so CoreDNS can bind to port 53. This is required on Ubuntu/Debian systems
|
||||
// where systemd-resolved listens on 127.0.0.53:53 by default.
|
||||
func (ci *CoreDNSInstaller) DisableResolvedStubListener() error {
|
||||
// Check if systemd-resolved is running
|
||||
if err := exec.Command("systemctl", "is-active", "--quiet", "systemd-resolved").Run(); err != nil {
|
||||
return nil // Not running, nothing to do
|
||||
}
|
||||
|
||||
fmt.Fprintf(ci.logWriter, " Disabling systemd-resolved DNS stub listener (for CoreDNS)...\n")
|
||||
|
||||
// Disable the stub listener
|
||||
resolvedConf := "/etc/systemd/resolved.conf.d/no-stub.conf"
|
||||
if err := os.MkdirAll("/etc/systemd/resolved.conf.d", 0755); err != nil {
|
||||
return fmt.Errorf("failed to create resolved.conf.d: %w", err)
|
||||
}
|
||||
conf := "[Resolve]\nDNSStubListener=no\n"
|
||||
if err := os.WriteFile(resolvedConf, []byte(conf), 0644); err != nil {
|
||||
return fmt.Errorf("failed to write resolved config: %w", err)
|
||||
}
|
||||
|
||||
// Point resolv.conf to localhost (CoreDNS) and a fallback
|
||||
resolvConf := "nameserver 127.0.0.1\nnameserver 8.8.8.8\n"
|
||||
if err := os.Remove("/etc/resolv.conf"); err != nil && !os.IsNotExist(err) {
|
||||
// It might be a symlink
|
||||
fmt.Fprintf(ci.logWriter, " ⚠️ Could not remove /etc/resolv.conf: %v\n", err)
|
||||
}
|
||||
if err := os.WriteFile("/etc/resolv.conf", []byte(resolvConf), 0644); err != nil {
|
||||
return fmt.Errorf("failed to write resolv.conf: %w", err)
|
||||
}
|
||||
|
||||
// Restart systemd-resolved
|
||||
if output, err := exec.Command("systemctl", "restart", "systemd-resolved").CombinedOutput(); err != nil {
|
||||
fmt.Fprintf(ci.logWriter, " ⚠️ Failed to restart systemd-resolved: %v (%s)\n", err, string(output))
|
||||
}
|
||||
|
||||
fmt.Fprintf(ci.logWriter, " ✓ systemd-resolved stub listener disabled\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ci *CoreDNSInstaller) Install() error {
|
||||
if ci.IsInstalled() {
|
||||
fmt.Fprintf(ci.logWriter, " ✓ CoreDNS with RQLite plugin already installed\n")
|
||||
|
||||
@ -222,14 +222,21 @@ func (gi *GatewayInstaller) InstallDeBrosBinaries(branch string, oramaHome strin
|
||||
|
||||
// InstallGo downloads and installs Go toolchain
|
||||
func (gi *GatewayInstaller) InstallGo() error {
|
||||
if _, err := exec.LookPath("go"); err == nil {
|
||||
fmt.Fprintf(gi.logWriter, " ✓ Go already installed\n")
|
||||
return nil
|
||||
requiredVersion := "1.22.5"
|
||||
if goPath, err := exec.LookPath("go"); err == nil {
|
||||
// Check version - upgrade if too old
|
||||
out, _ := exec.Command(goPath, "version").Output()
|
||||
if strings.Contains(string(out), "go"+requiredVersion) || strings.Contains(string(out), "go1.23") || strings.Contains(string(out), "go1.24") {
|
||||
fmt.Fprintf(gi.logWriter, " ✓ Go already installed (%s)\n", strings.TrimSpace(string(out)))
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(gi.logWriter, " Upgrading Go (current: %s, need >= %s)...\n", strings.TrimSpace(string(out)), requiredVersion)
|
||||
os.RemoveAll("/usr/local/go")
|
||||
} else {
|
||||
fmt.Fprintf(gi.logWriter, " Installing Go...\n")
|
||||
}
|
||||
|
||||
fmt.Fprintf(gi.logWriter, " Installing Go...\n")
|
||||
|
||||
goTarball := fmt.Sprintf("go1.22.5.linux-%s.tar.gz", gi.arch)
|
||||
goTarball := fmt.Sprintf("go%s.linux-%s.tar.gz", requiredVersion, gi.arch)
|
||||
goURL := fmt.Sprintf("https://go.dev/dl/%s", goTarball)
|
||||
|
||||
// Download
|
||||
|
||||
@ -640,9 +640,11 @@ func (ps *ProductionSetup) Phase5CreateSystemdServices(enableHTTPS bool) error {
|
||||
// Caddy service (for SSL/TLS with DNS-01 ACME challenges)
|
||||
if _, err := os.Stat("/usr/bin/caddy"); err == nil {
|
||||
// Create caddy user if it doesn't exist
|
||||
exec.Command("useradd", "-r", "-s", "/sbin/nologin", "caddy").Run()
|
||||
exec.Command("useradd", "-r", "-m", "-d", "/home/caddy", "-s", "/sbin/nologin", "caddy").Run()
|
||||
exec.Command("mkdir", "-p", "/var/lib/caddy").Run()
|
||||
exec.Command("chown", "caddy:caddy", "/var/lib/caddy").Run()
|
||||
exec.Command("mkdir", "-p", "/home/caddy").Run()
|
||||
exec.Command("chown", "caddy:caddy", "/home/caddy").Run()
|
||||
|
||||
caddyUnit := ps.serviceGenerator.GenerateCaddyService()
|
||||
if err := ps.serviceController.WriteServiceUnit("caddy.service", caddyUnit); err != nil {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user