Integrate BACKBEAT SDK and resolve KACHING license validation
Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -7,8 +7,8 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"chorus.services/bzzz/logging"
|
||||
"chorus.services/bzzz/pubsub"
|
||||
"chorus/internal/logging"
|
||||
"chorus/pubsub"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
|
||||
@@ -16,12 +16,12 @@ import (
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
"chorus.services/bzzz/pkg/config"
|
||||
"chorus.services/bzzz/pkg/security"
|
||||
"chorus.services/bzzz/repository"
|
||||
"chorus/pkg/config"
|
||||
"chorus/pkg/security"
|
||||
"chorus/pkg/repository"
|
||||
)
|
||||
|
||||
// SetupManager handles the initial configuration setup for BZZZ
|
||||
// SetupManager handles the initial configuration setup for CHORUS
|
||||
type SetupManager struct {
|
||||
configPath string
|
||||
factory repository.ProviderFactory
|
||||
@@ -514,11 +514,11 @@ func (s *SetupManager) testRepositoryConnection(repoConfig *RepositoryConfig) er
|
||||
AccessToken: repoConfig.AccessToken,
|
||||
Owner: repoConfig.Owner,
|
||||
Repository: repoConfig.Repository,
|
||||
TaskLabel: "bzzz-task",
|
||||
TaskLabel: "CHORUS-task",
|
||||
InProgressLabel: "in-progress",
|
||||
CompletedLabel: "completed",
|
||||
BaseBranch: "main",
|
||||
BranchPrefix: "bzzz/",
|
||||
BranchPrefix: "CHORUS/",
|
||||
}
|
||||
|
||||
provider, err := s.factory.CreateProvider(ctx, config)
|
||||
@@ -527,7 +527,7 @@ func (s *SetupManager) testRepositoryConnection(repoConfig *RepositoryConfig) er
|
||||
}
|
||||
|
||||
// Try to list tasks to test connection
|
||||
_, err = provider.ListAvailableTasks()
|
||||
_, err = provider.ListAvailableTasks(1) // Use project ID 1 for testing
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to repository: %w", err)
|
||||
}
|
||||
@@ -873,7 +873,7 @@ type DeploymentStep struct {
|
||||
Verified bool `json:"verified"`
|
||||
}
|
||||
|
||||
// DeployServiceToMachine deploys BZZZ service to a remote machine with full verification
|
||||
// DeployServiceToMachine deploys CHORUS service to a remote machine with full verification
|
||||
func (s *SetupManager) DeployServiceToMachine(ip string, privateKey string, username string, password string, port int, config interface{}) (*DeploymentResult, error) {
|
||||
result := &DeploymentResult{
|
||||
Steps: []DeploymentStep{},
|
||||
@@ -1137,8 +1137,8 @@ func (s *SetupManager) verifiedPreDeploymentCheck(client *ssh.Client, config int
|
||||
// Store system info for other steps to use
|
||||
result.SystemInfo = sysInfo
|
||||
|
||||
// Check for existing BZZZ processes (informational only - cleanup step will handle)
|
||||
output, err := s.executeSSHCommand(client, "ps aux | grep bzzz | grep -v grep || echo 'No BZZZ processes found'")
|
||||
// Check for existing CHORUS processes (informational only - cleanup step will handle)
|
||||
output, err := s.executeSSHCommand(client, "ps aux | grep CHORUS | grep -v grep || echo 'No CHORUS processes found'")
|
||||
if err != nil {
|
||||
s.updateLastStep(result, "failed", "process check", output, fmt.Sprintf("Failed to check processes: %v", err), false)
|
||||
return fmt.Errorf("pre-deployment check failed: %v", err)
|
||||
@@ -1146,14 +1146,14 @@ func (s *SetupManager) verifiedPreDeploymentCheck(client *ssh.Client, config int
|
||||
|
||||
// Log existing processes but don't fail - cleanup step will handle this
|
||||
var processStatus string
|
||||
if !strings.Contains(output, "No BZZZ processes found") {
|
||||
processStatus = "Existing BZZZ processes detected (will be stopped in cleanup step)"
|
||||
if !strings.Contains(output, "No CHORUS processes found") {
|
||||
processStatus = "Existing CHORUS processes detected (will be stopped in cleanup step)"
|
||||
} else {
|
||||
processStatus = "No existing BZZZ processes detected"
|
||||
processStatus = "No existing CHORUS processes detected"
|
||||
}
|
||||
|
||||
// Check for existing systemd services
|
||||
output2, _ := s.executeSSHCommand(client, "systemctl status bzzz 2>/dev/null || echo 'No BZZZ service'")
|
||||
output2, _ := s.executeSSHCommand(client, "systemctl status CHORUS 2>/dev/null || echo 'No CHORUS service'")
|
||||
|
||||
// Check system requirements
|
||||
output3, _ := s.executeSSHCommand(client, "uname -a && free -m && df -h /tmp")
|
||||
@@ -1163,29 +1163,29 @@ func (s *SetupManager) verifiedPreDeploymentCheck(client *ssh.Client, config int
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifiedStopExistingServices stops any existing BZZZ services
|
||||
// verifiedStopExistingServices stops any existing CHORUS services
|
||||
func (s *SetupManager) verifiedStopExistingServices(client *ssh.Client, config interface{}, password string, result *DeploymentResult) error {
|
||||
stepName := "Stop & Remove Existing Services"
|
||||
s.addStep(result, stepName, "running", "", "", "", false)
|
||||
|
||||
// Stop systemd service if exists
|
||||
cmd1 := "systemctl stop bzzz 2>/dev/null || echo 'No systemd service to stop'"
|
||||
cmd1 := "systemctl stop CHORUS 2>/dev/null || echo 'No systemd service to stop'"
|
||||
output1, _ := s.executeSudoCommand(client, password, cmd1)
|
||||
|
||||
// Disable systemd service if exists - separate command for better error tracking
|
||||
cmd2a := "systemctl disable bzzz 2>/dev/null || echo 'No systemd service to disable'"
|
||||
cmd2a := "systemctl disable CHORUS 2>/dev/null || echo 'No systemd service to disable'"
|
||||
output2a, _ := s.executeSudoCommand(client, password, cmd2a)
|
||||
|
||||
// Remove service files
|
||||
cmd2b := "rm -f /etc/systemd/system/bzzz.service ~/.config/systemd/user/bzzz.service 2>/dev/null || echo 'No service file to remove'"
|
||||
cmd2b := "rm -f /etc/systemd/system/CHORUS.service ~/.config/systemd/user/CHORUS.service 2>/dev/null || echo 'No service file to remove'"
|
||||
output2b, _ := s.executeSudoCommand(client, password, cmd2b)
|
||||
|
||||
// Kill any remaining processes
|
||||
cmd3 := "pkill -f bzzz || echo 'No processes to kill'"
|
||||
cmd3 := "pkill -f CHORUS || echo 'No processes to kill'"
|
||||
output3, _ := s.executeSSHCommand(client, cmd3)
|
||||
|
||||
// Remove old binaries from standard locations
|
||||
cmd4 := "rm -f /usr/local/bin/bzzz ~/bin/bzzz ~/bzzz 2>/dev/null || echo 'No old binaries to remove'"
|
||||
cmd4 := "rm -f /usr/local/bin/CHORUS ~/bin/CHORUS ~/CHORUS 2>/dev/null || echo 'No old binaries to remove'"
|
||||
output4, _ := s.executeSudoCommand(client, password, cmd4)
|
||||
|
||||
// Reload systemd after changes
|
||||
@@ -1193,7 +1193,7 @@ func (s *SetupManager) verifiedStopExistingServices(client *ssh.Client, config i
|
||||
output5, _ := s.executeSudoCommand(client, password, cmd5)
|
||||
|
||||
// Verify no processes remain
|
||||
output6, err := s.executeSSHCommand(client, "ps aux | grep bzzz | grep -v grep || echo 'All BZZZ processes stopped'")
|
||||
output6, err := s.executeSSHCommand(client, "ps aux | grep CHORUS | grep -v grep || echo 'All CHORUS processes stopped'")
|
||||
if err != nil {
|
||||
combinedOutput := fmt.Sprintf("Stop service:\n%s\n\nDisable service:\n%s\n\nRemove service files:\n%s\n\nKill processes:\n%s\n\nRemove binaries:\n%s\n\nReload systemd:\n%s\n\nVerification:\n%s",
|
||||
output1, output2a, output2b, output3, output4, output5, output6)
|
||||
@@ -1201,11 +1201,11 @@ func (s *SetupManager) verifiedStopExistingServices(client *ssh.Client, config i
|
||||
return fmt.Errorf("failed to verify process cleanup: %v", err)
|
||||
}
|
||||
|
||||
if !strings.Contains(output6, "All BZZZ processes stopped") {
|
||||
if !strings.Contains(output6, "All CHORUS processes stopped") {
|
||||
combinedOutput := fmt.Sprintf("Stop service:\n%s\n\nDisable service:\n%s\n\nRemove service files:\n%s\n\nKill processes:\n%s\n\nRemove binaries:\n%s\n\nReload systemd:\n%s\n\nVerification:\n%s",
|
||||
output1, output2a, output2b, output3, output4, output5, output6)
|
||||
s.updateLastStep(result, "failed", "process verification", combinedOutput, "BZZZ processes still running after cleanup", false)
|
||||
return fmt.Errorf("failed to stop all BZZZ processes")
|
||||
s.updateLastStep(result, "failed", "process verification", combinedOutput, "CHORUS processes still running after cleanup", false)
|
||||
return fmt.Errorf("failed to stop all CHORUS processes")
|
||||
}
|
||||
|
||||
combinedOutput := fmt.Sprintf("Stop service:\n%s\n\nDisable service:\n%s\n\nRemove service files:\n%s\n\nKill processes:\n%s\n\nRemove binaries:\n%s\n\nReload systemd:\n%s\n\nVerification:\n%s",
|
||||
@@ -1237,17 +1237,17 @@ func (s *SetupManager) performRollbackWithPassword(client *ssh.Client, password
|
||||
result.RollbackLog = append(result.RollbackLog, "Starting rollback procedure...")
|
||||
|
||||
// Stop any services we might have started
|
||||
if output, err := s.executeSudoCommand(client, password, "systemctl stop bzzz 2>/dev/null || echo 'No service to stop'"); err == nil {
|
||||
if output, err := s.executeSudoCommand(client, password, "systemctl stop CHORUS 2>/dev/null || echo 'No service to stop'"); err == nil {
|
||||
result.RollbackLog = append(result.RollbackLog, "Stopped service: "+output)
|
||||
}
|
||||
|
||||
// Remove systemd service
|
||||
if output, err := s.executeSudoCommand(client, password, "systemctl disable bzzz 2>/dev/null; rm -f /etc/systemd/system/bzzz.service 2>/dev/null || echo 'No service file to remove'"); err == nil {
|
||||
if output, err := s.executeSudoCommand(client, password, "systemctl disable CHORUS 2>/dev/null; rm -f /etc/systemd/system/CHORUS.service 2>/dev/null || echo 'No service file to remove'"); err == nil {
|
||||
result.RollbackLog = append(result.RollbackLog, "Removed service: "+output)
|
||||
}
|
||||
|
||||
// Remove binary
|
||||
if output, err := s.executeSudoCommand(client, password, "rm -f /usr/local/bin/bzzz 2>/dev/null || echo 'No binary to remove'"); err == nil {
|
||||
if output, err := s.executeSudoCommand(client, password, "rm -f /usr/local/bin/CHORUS 2>/dev/null || echo 'No binary to remove'"); err == nil {
|
||||
result.RollbackLog = append(result.RollbackLog, "Removed binary: "+output)
|
||||
}
|
||||
|
||||
@@ -1262,19 +1262,19 @@ func (s *SetupManager) performRollback(client *ssh.Client, result *DeploymentRes
|
||||
result.RollbackLog = append(result.RollbackLog, "Starting rollback procedure...")
|
||||
|
||||
// Stop any services we might have started
|
||||
if output, err := s.executeSSHCommand(client, "sudo -n systemctl stop bzzz 2>/dev/null || echo 'No service to stop'"); err == nil {
|
||||
if output, err := s.executeSSHCommand(client, "sudo -n systemctl stop CHORUS 2>/dev/null || echo 'No service to stop'"); err == nil {
|
||||
result.RollbackLog = append(result.RollbackLog, "Stopped service: "+output)
|
||||
}
|
||||
|
||||
// Remove binaries we might have copied
|
||||
if output, err := s.executeSSHCommand(client, "rm -f ~/bzzz /usr/local/bin/bzzz 2>/dev/null || echo 'No binaries to remove'"); err == nil {
|
||||
if output, err := s.executeSSHCommand(client, "rm -f ~/CHORUS /usr/local/bin/CHORUS 2>/dev/null || echo 'No binaries to remove'"); err == nil {
|
||||
result.RollbackLog = append(result.RollbackLog, "Removed binaries: "+output)
|
||||
}
|
||||
|
||||
result.RollbackLog = append(result.RollbackLog, "Rollback completed")
|
||||
}
|
||||
|
||||
// verifiedCopyBinary copies BZZZ binary and verifies installation
|
||||
// verifiedCopyBinary copies CHORUS binary and verifies installation
|
||||
func (s *SetupManager) verifiedCopyBinary(client *ssh.Client, config interface{}, password string, result *DeploymentResult) error {
|
||||
stepName := "Copy Binary"
|
||||
s.addStep(result, stepName, "running", "", "", "", false)
|
||||
@@ -1286,15 +1286,15 @@ func (s *SetupManager) verifiedCopyBinary(client *ssh.Client, config interface{}
|
||||
}
|
||||
|
||||
// Verify binary was copied and is executable
|
||||
checkCmd := "ls -la /usr/local/bin/bzzz ~/bin/bzzz 2>/dev/null || echo 'Binary not found in expected locations'"
|
||||
checkCmd := "ls -la /usr/local/bin/CHORUS ~/bin/CHORUS 2>/dev/null || echo 'Binary not found in expected locations'"
|
||||
output, err := s.executeSSHCommand(client, checkCmd)
|
||||
if err != nil {
|
||||
s.updateLastStep(result, "failed", checkCmd, output, fmt.Sprintf("Verification failed: %v", err), false)
|
||||
return fmt.Errorf("binary verification failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify binary can execute (note: BZZZ doesn't have --version flag, use --help)
|
||||
versionCmd := "timeout 3s /usr/local/bin/bzzz --help 2>&1 | head -n1 || timeout 3s ~/bin/bzzz --help 2>&1 | head -n1 || echo 'Binary not executable'"
|
||||
// Verify binary can execute (note: CHORUS doesn't have --version flag, use --help)
|
||||
versionCmd := "timeout 3s /usr/local/bin/CHORUS --help 2>&1 | head -n1 || timeout 3s ~/bin/CHORUS --help 2>&1 | head -n1 || echo 'Binary not executable'"
|
||||
versionOutput, _ := s.executeSSHCommand(client, versionCmd)
|
||||
|
||||
combinedOutput := fmt.Sprintf("File check:\n%s\n\nBinary test:\n%s", output, versionOutput)
|
||||
@@ -1320,7 +1320,7 @@ func (s *SetupManager) verifiedDeployConfiguration(client *ssh.Client, config in
|
||||
}
|
||||
|
||||
// Verify configuration file was created and is valid YAML
|
||||
verifyCmd := "ls -la ~/.bzzz/config.yaml && echo '--- Config Preview ---' && head -20 ~/.bzzz/config.yaml"
|
||||
verifyCmd := "ls -la ~/.CHORUS/config.yaml && echo '--- Config Preview ---' && head -20 ~/.CHORUS/config.yaml"
|
||||
output, err := s.executeSSHCommand(client, verifyCmd)
|
||||
if err != nil {
|
||||
s.updateLastStep(result, "failed", verifyCmd, output, fmt.Sprintf("Config verification failed: %v", err), false)
|
||||
@@ -1368,11 +1368,11 @@ func (s *SetupManager) verifiedCreateSystemdService(client *ssh.Client, config i
|
||||
}
|
||||
|
||||
// Verify service file was created and contains correct paths
|
||||
verifyCmd := "systemctl cat bzzz"
|
||||
verifyCmd := "systemctl cat CHORUS"
|
||||
output, err := s.executeSudoCommand(client, password, verifyCmd)
|
||||
if err != nil {
|
||||
// Try to check if the service file exists another way
|
||||
checkCmd := "ls -la /etc/systemd/system/bzzz.service"
|
||||
checkCmd := "ls -la /etc/systemd/system/CHORUS.service"
|
||||
checkOutput, checkErr := s.executeSudoCommand(client, password, checkCmd)
|
||||
if checkErr != nil {
|
||||
s.updateLastStep(result, "failed", verifyCmd, output, fmt.Sprintf("Service verification failed: %v. Service file check also failed: %v", err, checkErr), false)
|
||||
@@ -1382,7 +1382,7 @@ func (s *SetupManager) verifiedCreateSystemdService(client *ssh.Client, config i
|
||||
}
|
||||
|
||||
// Verify service can be enabled
|
||||
enableCmd := "systemctl enable bzzz"
|
||||
enableCmd := "systemctl enable CHORUS"
|
||||
enableOutput, enableErr := s.executeSudoCommand(client, password, enableCmd)
|
||||
if enableErr != nil {
|
||||
combinedOutput := fmt.Sprintf("Service file:\n%s\n\nEnable attempt:\n%s", output, enableOutput)
|
||||
@@ -1411,25 +1411,25 @@ func (s *SetupManager) verifiedStartService(client *ssh.Client, config interface
|
||||
s.addStep(result, "Pre-Start Checks", "running", "", "", "", false)
|
||||
|
||||
// Check if config file exists and is readable by the service user
|
||||
configCheck := "ls -la /home/*/bzzz/config.yaml 2>/dev/null || echo 'Config file not found'"
|
||||
configCheck := "ls -la /home/*/CHORUS/config.yaml 2>/dev/null || echo 'Config file not found'"
|
||||
configOutput, _ := s.executeSSHCommand(client, configCheck)
|
||||
|
||||
// Check if binary is executable
|
||||
binCheck := "ls -la /usr/local/bin/bzzz"
|
||||
binCheck := "ls -la /usr/local/bin/CHORUS"
|
||||
binOutput, _ := s.executeSudoCommand(client, password, binCheck)
|
||||
|
||||
preflightInfo := fmt.Sprintf("Binary check:\n%s\n\nConfig check:\n%s", binOutput, configOutput)
|
||||
s.updateLastStep(result, "success", "pre-flight", preflightInfo, "Pre-start checks completed", false)
|
||||
|
||||
// Start the service
|
||||
startCmd := "systemctl start bzzz"
|
||||
startCmd := "systemctl start CHORUS"
|
||||
startOutput, err := s.executeSudoCommand(client, password, startCmd)
|
||||
if err != nil {
|
||||
// Get detailed error information
|
||||
statusCmd := "systemctl status bzzz"
|
||||
statusCmd := "systemctl status CHORUS"
|
||||
statusOutput, _ := s.executeSudoCommand(client, password, statusCmd)
|
||||
|
||||
logsCmd := "journalctl -u bzzz --no-pager -n 20"
|
||||
logsCmd := "journalctl -u CHORUS --no-pager -n 20"
|
||||
logsOutput, _ := s.executeSudoCommand(client, password, logsCmd)
|
||||
|
||||
// Combine all error information
|
||||
@@ -1440,21 +1440,21 @@ func (s *SetupManager) verifiedStartService(client *ssh.Client, config interface
|
||||
return fmt.Errorf("failed to start systemd service: %v", err)
|
||||
}
|
||||
|
||||
// Wait for service to fully initialize (BZZZ needs time to start all subsystems)
|
||||
// Wait for service to fully initialize (CHORUS needs time to start all subsystems)
|
||||
time.Sleep(8 * time.Second)
|
||||
|
||||
// Verify service is running
|
||||
statusCmd := "systemctl status bzzz"
|
||||
statusCmd := "systemctl status CHORUS"
|
||||
statusOutput, _ := s.executeSSHCommand(client, statusCmd)
|
||||
|
||||
// Check if service is active
|
||||
if !strings.Contains(statusOutput, "active (running)") {
|
||||
// Get detailed logs to understand why service failed
|
||||
logsCmd := "journalctl -u bzzz --no-pager -n 20"
|
||||
logsCmd := "journalctl -u CHORUS --no-pager -n 20"
|
||||
logsOutput, _ := s.executeSudoCommand(client, password, logsCmd)
|
||||
|
||||
// Check if config file exists and is readable
|
||||
configCheckCmd := "ls -la ~/.bzzz/config.yaml && head -5 ~/.bzzz/config.yaml"
|
||||
configCheckCmd := "ls -la ~/.CHORUS/config.yaml && head -5 ~/.CHORUS/config.yaml"
|
||||
configCheckOutput, _ := s.executeSSHCommand(client, configCheckCmd)
|
||||
|
||||
combinedOutput := fmt.Sprintf("Start attempt:\n%s\n\nStatus check:\n%s\n\nRecent logs:\n%s\n\nConfig check:\n%s",
|
||||
@@ -1474,12 +1474,12 @@ func (s *SetupManager) verifiedPostDeploymentTest(client *ssh.Client, config int
|
||||
s.addStep(result, stepName, "running", "", "", "", false)
|
||||
|
||||
// Test 1: Verify binary is executable
|
||||
// Note: BZZZ binary doesn't have --version flag, so just check if it's executable and can start help
|
||||
versionCmd := "if pgrep -f bzzz >/dev/null; then echo 'BZZZ process running'; else timeout 3s /usr/local/bin/bzzz --help 2>&1 | head -n1 || timeout 3s ~/bin/bzzz --help 2>&1 | head -n1 || echo 'Binary not executable'; fi"
|
||||
// Note: CHORUS binary doesn't have --version flag, so just check if it's executable and can start help
|
||||
versionCmd := "if pgrep -f CHORUS >/dev/null; then echo 'CHORUS process running'; else timeout 3s /usr/local/bin/CHORUS --help 2>&1 | head -n1 || timeout 3s ~/bin/CHORUS --help 2>&1 | head -n1 || echo 'Binary not executable'; fi"
|
||||
versionOutput, _ := s.executeSSHCommand(client, versionCmd)
|
||||
|
||||
// Test 2: Verify service status
|
||||
serviceCmd := "systemctl status bzzz --no-pager"
|
||||
serviceCmd := "systemctl status CHORUS --no-pager"
|
||||
serviceOutput, _ := s.executeSSHCommand(client, serviceCmd)
|
||||
|
||||
// Test 3: Wait for API to be ready, then check if setup API is responding
|
||||
@@ -1503,15 +1503,15 @@ func (s *SetupManager) verifiedPostDeploymentTest(client *ssh.Client, config int
|
||||
}
|
||||
|
||||
// Test 4: Verify configuration is readable
|
||||
configCmd := "test -r ~/.bzzz/config.yaml && echo 'Config readable' || echo 'Config not readable'"
|
||||
configCmd := "test -r ~/.CHORUS/config.yaml && echo 'Config readable' || echo 'Config not readable'"
|
||||
configOutput, _ := s.executeSSHCommand(client, configCmd)
|
||||
|
||||
combinedOutput := fmt.Sprintf("Binary test:\n%s\n\nService test:\n%s\n\nAPI test:\n%s\n\nConfig test:\n%s",
|
||||
versionOutput, serviceOutput, apiOutput, configOutput)
|
||||
|
||||
// Determine if tests passed and provide detailed failure information
|
||||
// Binary test passes if BZZZ is running OR if help command succeeded
|
||||
binaryFailed := strings.Contains(versionOutput, "Binary not executable") && !strings.Contains(versionOutput, "BZZZ process running")
|
||||
// Binary test passes if CHORUS is running OR if help command succeeded
|
||||
binaryFailed := strings.Contains(versionOutput, "Binary not executable") && !strings.Contains(versionOutput, "CHORUS process running")
|
||||
configFailed := strings.Contains(configOutput, "Config not readable")
|
||||
|
||||
if binaryFailed || configFailed {
|
||||
@@ -1532,7 +1532,7 @@ func (s *SetupManager) verifiedPostDeploymentTest(client *ssh.Client, config int
|
||||
return nil
|
||||
}
|
||||
|
||||
// copyBinaryToMachineWithPassword copies the BZZZ binary to remote machine using SCP protocol with sudo password
|
||||
// copyBinaryToMachineWithPassword copies the CHORUS binary to remote machine using SCP protocol with sudo password
|
||||
func (s *SetupManager) copyBinaryToMachineWithPassword(client *ssh.Client, password string) error {
|
||||
// Read current binary
|
||||
binaryPath, err := os.Executable()
|
||||
@@ -1567,12 +1567,12 @@ func (s *SetupManager) copyBinaryToMachineWithPassword(client *ssh.Client, passw
|
||||
}
|
||||
|
||||
// Start SCP receive command on remote host
|
||||
remotePath := "~/bzzz"
|
||||
remotePath := "~/CHORUS"
|
||||
go func() {
|
||||
defer stdin.Close()
|
||||
|
||||
// Send SCP header: C<mode> <size> <filename>\n
|
||||
header := fmt.Sprintf("C0755 %d bzzz\n", len(binaryData))
|
||||
header := fmt.Sprintf("C0755 %d CHORUS\n", len(binaryData))
|
||||
stdin.Write([]byte(header))
|
||||
|
||||
// Wait for acknowledgment
|
||||
@@ -1602,7 +1602,7 @@ func (s *SetupManager) copyBinaryToMachineWithPassword(client *ssh.Client, passw
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
if err := session.Run("chmod +x ~/bzzz"); err != nil {
|
||||
if err := session.Run("chmod +x ~/CHORUS"); err != nil {
|
||||
return fmt.Errorf("failed to make binary executable: %w", err)
|
||||
}
|
||||
|
||||
@@ -1617,11 +1617,11 @@ func (s *SetupManager) copyBinaryToMachineWithPassword(client *ssh.Client, passw
|
||||
var sudoCmd string
|
||||
if password == "" {
|
||||
// Try passwordless sudo first
|
||||
sudoCmd = "sudo -n mv ~/bzzz /usr/local/bin/bzzz && sudo -n chmod +x /usr/local/bin/bzzz"
|
||||
sudoCmd = "sudo -n mv ~/CHORUS /usr/local/bin/CHORUS && sudo -n chmod +x /usr/local/bin/CHORUS"
|
||||
} else {
|
||||
// Use password sudo
|
||||
escapedPassword := strings.ReplaceAll(password, "'", "'\"'\"'")
|
||||
sudoCmd = fmt.Sprintf("echo '%s' | sudo -S mv ~/bzzz /usr/local/bin/bzzz && echo '%s' | sudo -S chmod +x /usr/local/bin/bzzz",
|
||||
sudoCmd = fmt.Sprintf("echo '%s' | sudo -S mv ~/CHORUS /usr/local/bin/CHORUS && echo '%s' | sudo -S chmod +x /usr/local/bin/CHORUS",
|
||||
escapedPassword, escapedPassword)
|
||||
}
|
||||
|
||||
@@ -1634,7 +1634,7 @@ func (s *SetupManager) copyBinaryToMachineWithPassword(client *ssh.Client, passw
|
||||
defer session.Close()
|
||||
|
||||
// Create ~/bin directory and add to PATH if it doesn't exist
|
||||
if err := session.Run("mkdir -p ~/bin && mv ~/bzzz ~/bin/bzzz && chmod +x ~/bin/bzzz"); err != nil {
|
||||
if err := session.Run("mkdir -p ~/bin && mv ~/CHORUS ~/bin/CHORUS && chmod +x ~/bin/CHORUS"); err != nil {
|
||||
return fmt.Errorf("failed to install binary to ~/bin: %w", err)
|
||||
}
|
||||
|
||||
@@ -1651,7 +1651,7 @@ func (s *SetupManager) copyBinaryToMachineWithPassword(client *ssh.Client, passw
|
||||
return nil
|
||||
}
|
||||
|
||||
// copyBinaryToMachine copies the BZZZ binary to remote machine using SCP protocol (passwordless sudo)
|
||||
// copyBinaryToMachine copies the CHORUS binary to remote machine using SCP protocol (passwordless sudo)
|
||||
func (s *SetupManager) copyBinaryToMachine(client *ssh.Client) error {
|
||||
return s.copyBinaryToMachineWithPassword(client, "")
|
||||
}
|
||||
@@ -1669,8 +1669,8 @@ func (s *SetupManager) createSystemdServiceWithPassword(client *ssh.Client, conf
|
||||
session.Stdout = &stdout
|
||||
|
||||
// Check where the binary was installed
|
||||
binaryPath := "/usr/local/bin/bzzz"
|
||||
if err := session.Run("test -f /usr/local/bin/bzzz"); err != nil {
|
||||
binaryPath := "/usr/local/bin/CHORUS"
|
||||
if err := session.Run("test -f /usr/local/bin/CHORUS"); err != nil {
|
||||
// If not in /usr/local/bin, it should be in ~/bin
|
||||
session, err = client.NewSession()
|
||||
if err != nil {
|
||||
@@ -1679,7 +1679,7 @@ func (s *SetupManager) createSystemdServiceWithPassword(client *ssh.Client, conf
|
||||
defer session.Close()
|
||||
|
||||
session.Stdout = &stdout
|
||||
if err := session.Run("echo $HOME/bin/bzzz"); err == nil {
|
||||
if err := session.Run("echo $HOME/bin/CHORUS"); err == nil {
|
||||
binaryPath = strings.TrimSpace(stdout.String())
|
||||
}
|
||||
}
|
||||
@@ -1700,13 +1700,13 @@ func (s *SetupManager) createSystemdServiceWithPassword(client *ssh.Client, conf
|
||||
|
||||
// Create service file with actual username
|
||||
serviceFile := fmt.Sprintf(`[Unit]
|
||||
Description=BZZZ P2P Task Coordination System
|
||||
Documentation=https://chorus.services/docs/bzzz
|
||||
Description=CHORUS P2P Task Coordination System
|
||||
Documentation=https://chorus.services/docs/CHORUS
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=%s --config /home/%s/.bzzz/config.yaml
|
||||
ExecStart=%s --config /home/%s/.CHORUS/config.yaml
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
User=%s
|
||||
@@ -1717,13 +1717,13 @@ WantedBy=multi-user.target
|
||||
`, binaryPath, username, username, username)
|
||||
|
||||
// Create service file in temp location first, then move with sudo
|
||||
createCmd := fmt.Sprintf("cat > /tmp/bzzz.service << 'EOF'\n%sEOF", serviceFile)
|
||||
createCmd := fmt.Sprintf("cat > /tmp/CHORUS.service << 'EOF'\n%sEOF", serviceFile)
|
||||
if _, err := s.executeSSHCommand(client, createCmd); err != nil {
|
||||
return fmt.Errorf("failed to create temp service file: %w", err)
|
||||
}
|
||||
|
||||
// Move to systemd directory using password sudo
|
||||
moveCmd := "mv /tmp/bzzz.service /etc/systemd/system/bzzz.service"
|
||||
moveCmd := "mv /tmp/CHORUS.service /etc/systemd/system/CHORUS.service"
|
||||
if _, err := s.executeSudoCommand(client, password, moveCmd); err != nil {
|
||||
return fmt.Errorf("failed to install system service file: %w", err)
|
||||
}
|
||||
@@ -1750,8 +1750,8 @@ func (s *SetupManager) createSystemdService(client *ssh.Client, config interface
|
||||
session.Stdout = &stdout
|
||||
|
||||
// Check where the binary was installed
|
||||
binaryPath := "/usr/local/bin/bzzz"
|
||||
if err := session.Run("test -f /usr/local/bin/bzzz"); err != nil {
|
||||
binaryPath := "/usr/local/bin/CHORUS"
|
||||
if err := session.Run("test -f /usr/local/bin/CHORUS"); err != nil {
|
||||
// If not in /usr/local/bin, it should be in ~/bin
|
||||
session, err = client.NewSession()
|
||||
if err != nil {
|
||||
@@ -1760,20 +1760,20 @@ func (s *SetupManager) createSystemdService(client *ssh.Client, config interface
|
||||
defer session.Close()
|
||||
|
||||
session.Stdout = &stdout
|
||||
if err := session.Run("echo $HOME/bin/bzzz"); err == nil {
|
||||
if err := session.Run("echo $HOME/bin/CHORUS"); err == nil {
|
||||
binaryPath = strings.TrimSpace(stdout.String())
|
||||
}
|
||||
}
|
||||
|
||||
// Create service file that works for both system and user services
|
||||
serviceFile := fmt.Sprintf(`[Unit]
|
||||
Description=BZZZ P2P Task Coordination System
|
||||
Documentation=https://chorus.services/docs/bzzz
|
||||
Description=CHORUS P2P Task Coordination System
|
||||
Documentation=https://chorus.services/docs/CHORUS
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=%s --config %%h/.bzzz/config.yaml
|
||||
ExecStart=%s --config %%h/.CHORUS/config.yaml
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Environment=HOME=%%h
|
||||
@@ -1790,7 +1790,7 @@ WantedBy=default.target
|
||||
defer session.Close()
|
||||
|
||||
// Create service file in temp location first, then move with sudo
|
||||
cmd := fmt.Sprintf("cat > /tmp/bzzz.service << 'EOF'\n%sEOF", serviceFile)
|
||||
cmd := fmt.Sprintf("cat > /tmp/CHORUS.service << 'EOF'\n%sEOF", serviceFile)
|
||||
if err := session.Run(cmd); err != nil {
|
||||
return fmt.Errorf("failed to create temp service file: %w", err)
|
||||
}
|
||||
@@ -1803,7 +1803,7 @@ WantedBy=default.target
|
||||
defer session.Close()
|
||||
|
||||
// Try passwordless sudo for system service
|
||||
if err := session.Run("sudo -n mv /tmp/bzzz.service /etc/systemd/system/bzzz.service"); err != nil {
|
||||
if err := session.Run("sudo -n mv /tmp/CHORUS.service /etc/systemd/system/CHORUS.service"); err != nil {
|
||||
// Sudo failed, create user-level service instead
|
||||
session, err = client.NewSession()
|
||||
if err != nil {
|
||||
@@ -1812,7 +1812,7 @@ WantedBy=default.target
|
||||
defer session.Close()
|
||||
|
||||
// Create user systemd directory and install service there
|
||||
if err := session.Run("mkdir -p ~/.config/systemd/user && mv /tmp/bzzz.service ~/.config/systemd/user/bzzz.service"); err != nil {
|
||||
if err := session.Run("mkdir -p ~/.config/systemd/user && mv /tmp/CHORUS.service ~/.config/systemd/user/CHORUS.service"); err != nil {
|
||||
return fmt.Errorf("failed to install user service file: %w", err)
|
||||
}
|
||||
|
||||
@@ -1823,8 +1823,8 @@ WantedBy=default.target
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
if err := session.Run("systemctl --user daemon-reload && systemctl --user enable bzzz"); err != nil {
|
||||
return fmt.Errorf("failed to enable user bzzz service: %w", err)
|
||||
if err := session.Run("systemctl --user daemon-reload && systemctl --user enable CHORUS"); err != nil {
|
||||
return fmt.Errorf("failed to enable user CHORUS service: %w", err)
|
||||
}
|
||||
|
||||
// Enable lingering so user services start at boot
|
||||
@@ -1844,8 +1844,8 @@ WantedBy=default.target
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
if err := session.Run("sudo -n useradd -r -s /bin/false bzzz 2>/dev/null || true"); err != nil {
|
||||
return fmt.Errorf("failed to create bzzz user: %w", err)
|
||||
if err := session.Run("sudo -n useradd -r -s /bin/false CHORUS 2>/dev/null || true"); err != nil {
|
||||
return fmt.Errorf("failed to create CHORUS user: %w", err)
|
||||
}
|
||||
|
||||
session, err = client.NewSession()
|
||||
@@ -1854,8 +1854,8 @@ WantedBy=default.target
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
if err := session.Run("sudo -n mkdir -p /opt/bzzz && sudo -n chown bzzz:bzzz /opt/bzzz"); err != nil {
|
||||
return fmt.Errorf("failed to create bzzz directory: %w", err)
|
||||
if err := session.Run("sudo -n mkdir -p /opt/CHORUS && sudo -n chown CHORUS:CHORUS /opt/CHORUS"); err != nil {
|
||||
return fmt.Errorf("failed to create CHORUS directory: %w", err)
|
||||
}
|
||||
|
||||
// Reload systemd and enable service
|
||||
@@ -1865,15 +1865,15 @@ WantedBy=default.target
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
if err := session.Run("sudo -n systemctl daemon-reload && sudo -n systemctl enable bzzz"); err != nil {
|
||||
return fmt.Errorf("failed to enable bzzz service: %w", err)
|
||||
if err := session.Run("sudo -n systemctl daemon-reload && sudo -n systemctl enable CHORUS"); err != nil {
|
||||
return fmt.Errorf("failed to enable CHORUS service: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// startService starts the BZZZ service (system or user level)
|
||||
// startService starts the CHORUS service (system or user level)
|
||||
func (s *SetupManager) startService(client *ssh.Client) error {
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
@@ -1882,7 +1882,7 @@ func (s *SetupManager) startService(client *ssh.Client) error {
|
||||
defer session.Close()
|
||||
|
||||
// Try system service first, fall back to user service
|
||||
if err := session.Run("sudo -n systemctl start bzzz"); err != nil {
|
||||
if err := session.Run("sudo -n systemctl start CHORUS"); err != nil {
|
||||
// Try user service instead
|
||||
session, err = client.NewSession()
|
||||
if err != nil {
|
||||
@@ -1890,7 +1890,7 @@ func (s *SetupManager) startService(client *ssh.Client) error {
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
return session.Run("systemctl --user start bzzz")
|
||||
return session.Run("systemctl --user start CHORUS")
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -1938,7 +1938,7 @@ func (s *SetupManager) GenerateConfigForMachine(machineIP string, config interfa
|
||||
}
|
||||
|
||||
// Generate YAML configuration that matches the Go struct layout
|
||||
configYAML := fmt.Sprintf(`# BZZZ Configuration for %s
|
||||
configYAML := fmt.Sprintf(`# CHORUS Configuration for %s
|
||||
whoosh_api:
|
||||
base_url: "https://whoosh.home.deepblack.cloud"
|
||||
timeout: 30s
|
||||
@@ -1953,7 +1953,7 @@ agent:
|
||||
specialization: "general_developer"
|
||||
model_selection_webhook: "https://n8n.home.deepblack.cloud/webhook/model-selection"
|
||||
default_reasoning_model: "phi3"
|
||||
sandbox_image: "registry.home.deepblack.cloud/bzzz-sandbox:latest"
|
||||
sandbox_image: "registry.home.deepblack.cloud/CHORUS-sandbox:latest"
|
||||
role: ""
|
||||
system_prompt: ""
|
||||
reports_to: []
|
||||
@@ -1976,8 +1976,8 @@ github:
|
||||
assignee: ""
|
||||
|
||||
p2p:
|
||||
service_tag: "bzzz-peer-discovery"
|
||||
bzzz_topic: "bzzz/coordination/v1"
|
||||
service_tag: "CHORUS-peer-discovery"
|
||||
bzzz_topic: "CHORUS/coordination/v1"
|
||||
hmmm_topic: "hmmm/meta-discussion/v1"
|
||||
discovery_timeout: 10s
|
||||
escalation_webhook: "https://n8n.home.deepblack.cloud/webhook-test/human-escalation"
|
||||
@@ -2011,7 +2011,7 @@ v2:
|
||||
enabled: false
|
||||
bootstrap_peers: []
|
||||
mode: "auto"
|
||||
protocol_prefix: "/bzzz"
|
||||
protocol_prefix: "/CHORUS"
|
||||
bootstrap_timeout: 30s
|
||||
discovery_interval: 1m0s
|
||||
auto_bootstrap: false
|
||||
@@ -2031,7 +2031,7 @@ ucxl:
|
||||
enabled: false
|
||||
server:
|
||||
port: 8081
|
||||
base_path: "/bzzz"
|
||||
base_path: "/CHORUS"
|
||||
enabled: true
|
||||
resolution:
|
||||
cache_ttl: 5m0s
|
||||
@@ -2039,12 +2039,12 @@ ucxl:
|
||||
max_results: 50
|
||||
storage:
|
||||
type: "filesystem"
|
||||
directory: "/tmp/bzzz-ucxl-storage"
|
||||
directory: "/tmp/CHORUS-ucxl-storage"
|
||||
max_size: 104857600
|
||||
p2p_integration:
|
||||
enable_announcement: true
|
||||
enable_discovery: true
|
||||
announcement_topic: "bzzz/ucxl/announcement/v1"
|
||||
announcement_topic: "CHORUS/ucxl/announcement/v1"
|
||||
discovery_timeout: 30s
|
||||
|
||||
security:
|
||||
@@ -2063,7 +2063,7 @@ security:
|
||||
conflict_resolution: "highest_uptime"
|
||||
key_rotation_days: 90
|
||||
audit_logging: true
|
||||
audit_path: ".bzzz/security-audit.log"
|
||||
audit_path: ".CHORUS/security-audit.log"
|
||||
|
||||
ai:
|
||||
ollama:
|
||||
@@ -2079,7 +2079,7 @@ ai:
|
||||
return configYAML, nil
|
||||
}
|
||||
|
||||
// GenerateConfigForMachineSimple generates a simple BZZZ configuration that matches the working config structure
|
||||
// GenerateConfigForMachineSimple generates a simple CHORUS configuration that matches the working config structure
|
||||
// REVENUE CRITICAL: This method now properly processes license data to enable revenue protection
|
||||
func (s *SetupManager) GenerateConfigForMachineSimple(machineIP string, config interface{}) (string, error) {
|
||||
// CRITICAL FIX: Extract license data from setup configuration - this was being ignored!
|
||||
@@ -2103,7 +2103,7 @@ func (s *SetupManager) GenerateConfigForMachineSimple(machineIP string, config i
|
||||
|
||||
// Validate license data exists - FAIL CLOSED DESIGN
|
||||
if licenseData == nil {
|
||||
return "", fmt.Errorf("REVENUE PROTECTION: License data missing from setup configuration - BZZZ cannot be deployed without valid licensing")
|
||||
return "", fmt.Errorf("REVENUE PROTECTION: License data missing from setup configuration - CHORUS cannot be deployed without valid licensing")
|
||||
}
|
||||
|
||||
// Extract required license fields with validation
|
||||
@@ -2112,14 +2112,14 @@ func (s *SetupManager) GenerateConfigForMachineSimple(machineIP string, config i
|
||||
orgName, _ := licenseData["organizationName"].(string)
|
||||
|
||||
if email == "" || licenseKey == "" {
|
||||
return "", fmt.Errorf("REVENUE PROTECTION: Email and license key are required - cannot deploy BZZZ without valid licensing")
|
||||
return "", fmt.Errorf("REVENUE PROTECTION: Email and license key are required - cannot deploy CHORUS without valid licensing")
|
||||
}
|
||||
|
||||
// Generate unique cluster ID for license binding (prevents license sharing across clusters)
|
||||
clusterID := fmt.Sprintf("cluster-%s-%d", hostname, time.Now().Unix())
|
||||
|
||||
// Generate YAML configuration with FULL license integration for revenue protection
|
||||
configYAML := fmt.Sprintf(`# BZZZ Configuration for %s - REVENUE PROTECTED
|
||||
configYAML := fmt.Sprintf(`# CHORUS Configuration for %s - REVENUE PROTECTED
|
||||
# Generated at %s with license validation
|
||||
whoosh_api:
|
||||
base_url: "https://whoosh.home.deepblack.cloud"
|
||||
@@ -2153,14 +2153,14 @@ agent:
|
||||
|
||||
github:
|
||||
token_file: ""
|
||||
user_agent: "BZZZ-Agent/1.0"
|
||||
user_agent: "CHORUS-Agent/1.0"
|
||||
timeout: 30s
|
||||
rate_limit: true
|
||||
assignee: ""
|
||||
|
||||
p2p:
|
||||
service_tag: "bzzz-peer-discovery"
|
||||
bzzz_topic: "bzzz/coordination/v1"
|
||||
service_tag: "CHORUS-peer-discovery"
|
||||
bzzz_topic: "CHORUS/coordination/v1"
|
||||
hmmm_topic: "hmmm/meta-discussion/v1"
|
||||
discovery_timeout: 10s
|
||||
escalation_webhook: ""
|
||||
@@ -2194,7 +2194,7 @@ v2:
|
||||
enabled: false
|
||||
bootstrap_peers: []
|
||||
mode: "auto"
|
||||
protocol_prefix: "/bzzz"
|
||||
protocol_prefix: "/CHORUS"
|
||||
bootstrap_timeout: 30s
|
||||
discovery_interval: 1m0s
|
||||
auto_bootstrap: false
|
||||
@@ -2214,7 +2214,7 @@ ucxl:
|
||||
enabled: false
|
||||
server:
|
||||
port: 8081
|
||||
base_path: "/bzzz"
|
||||
base_path: "/CHORUS"
|
||||
enabled: false
|
||||
resolution:
|
||||
cache_ttl: 5m0s
|
||||
@@ -2222,12 +2222,12 @@ ucxl:
|
||||
max_results: 50
|
||||
storage:
|
||||
type: "filesystem"
|
||||
directory: "/tmp/bzzz-ucxl-storage"
|
||||
directory: "/tmp/CHORUS-ucxl-storage"
|
||||
max_size: 104857600
|
||||
p2p_integration:
|
||||
enable_announcement: false
|
||||
enable_discovery: false
|
||||
announcement_topic: "bzzz/ucxl/announcement/v1"
|
||||
announcement_topic: "CHORUS/ucxl/announcement/v1"
|
||||
discovery_timeout: 30s
|
||||
|
||||
security:
|
||||
@@ -2308,7 +2308,7 @@ func (s *SetupManager) generateAndDeployConfig(client *ssh.Client, nodeIP string
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
if err := session.Run("mkdir -p ~/.bzzz ~/.bzzz/data ~/.bzzz/logs"); err != nil {
|
||||
if err := session.Run("mkdir -p ~/.CHORUS ~/.CHORUS/data ~/.CHORUS/logs"); err != nil {
|
||||
return fmt.Errorf("failed to create config directories: %w", err)
|
||||
}
|
||||
|
||||
@@ -2329,14 +2329,14 @@ func (s *SetupManager) generateAndDeployConfig(client *ssh.Client, nodeIP string
|
||||
stdin.Write([]byte(configYAML))
|
||||
}()
|
||||
|
||||
if err := session.Run("cat > ~/.bzzz/config.yaml"); err != nil {
|
||||
if err := session.Run("cat > ~/.CHORUS/config.yaml"); err != nil {
|
||||
return fmt.Errorf("failed to deploy config file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// configureFirewall configures firewall rules for BZZZ ports
|
||||
// configureFirewall configures firewall rules for CHORUS ports
|
||||
func (s *SetupManager) configureFirewall(client *ssh.Client, config interface{}) error {
|
||||
// Extract ports from configuration
|
||||
configMap, ok := config.(map[string]interface{})
|
||||
@@ -2346,7 +2346,7 @@ func (s *SetupManager) configureFirewall(client *ssh.Client, config interface{})
|
||||
|
||||
ports := []string{"22"} // Always include SSH
|
||||
|
||||
// Add BZZZ ports
|
||||
// Add CHORUS ports
|
||||
if portsConfig, exists := configMap["ports"]; exists {
|
||||
if portsMap, ok := portsConfig.(map[string]interface{}); ok {
|
||||
for _, value := range portsMap {
|
||||
|
||||
Reference in New Issue
Block a user