CodexMCP: SSH and SCP Processor Function
func RunRemoteCommand(vm VM, sshUser, sshKey, command string) error {
fmt.Printf("[INFO] Executing command on %s: %s\n", vm.Name, command)
var cmd *exec.Cmd
// Check if the command is an `scp` transfer
if strings.HasPrefix(command, "scp") {
cleanCmd := strings.Replace(command, "scp ", "", 1) // Remove "scp " only at the beginning
args := strings.Fields(cleanCmd) // Split remaining arguments into a slice
cmd = exec.Command("scp",
"-o", "StrictHostKeyChecking=no",
"-o", "UserKnownHostsFile=/dev/null",
"-i", sshKey,
)
cmd.Args = append(cmd.Args, args...) // Append the rest of the SCP arguments
} else {
cmd = exec.Command("ssh",
"-o", "StrictHostKeyChecking=no",
"-o", "UserKnownHostsFile=/dev/null",
"-i", sshKey,
fmt.Sprintf("%s@%s", sshUser, vm.IPAddress), command)
}
// Capture stdout and stderr separately
stdout, _ := cmd.StdoutPipe()
stderr, _ := cmd.StderrPipe()
if err := cmd.Start(); err != nil {
return fmt.Errorf("[ERROR] Failed to start command on %s: %v", vm.Name, err)
}
// Stream stdout
go func() {
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
fmt.Printf("[OUTPUT] %s\n", scanner.Text())
}
}()
// Stream stderr
go func() {
scanner := bufio.NewScanner(stderr)
for scanner.Scan() {
fmt.Printf("[ERROR] %s\n", scanner.Text())
}
}()
err := cmd.Wait()
if err != nil {
fmt.Printf("[ERROR] Command execution failed on %s: %v\n", vm.Name, err)
return err
}
return nil
}
Explanation
This function is responsible for executing remote commands on virtual machines using SSH. It also handles file transfers via SCP when necessary. This is a critical part of CodexMCP's automation, as it ensures that all provisioning and configuration tasks can be performed remotely without manual intervention.
Checking if the Command is an SCP Transfer
if strings.HasPrefix(command, "scp") {
cleanCmd := strings.Replace(command, "scp ", "", 1) // Remove "scp " only at the beginning
args := strings.Fields(cleanCmd) // Split remaining arguments into a slice
cmd = exec.Command("scp",
"-o", "StrictHostKeyChecking=no",
"-o", "UserKnownHostsFile=/dev/null",
"-i", sshKey,
)
cmd.Args = append(cmd.Args, args...) // Append the rest of the SCP arguments
}
This section detects if the command is an SCP file transfer. If it is, it removes the "scp" prefix, extracts the arguments, and executes SCP with the necessary SSH options.
StrictHostKeyChecking=no
ensures SSH does not prompt for unknown host keys, preventing the automation from stalling.UserKnownHostsFile=/dev/null
prevents saving host keys, avoiding conflicts when reusing IP addresses.-i sshKey
specifies the SSH private key used for authentication.- The remaining arguments (
args
) are passed directly to SCP, allowing flexible file transfers.
Executing SSH Commands
cmd = exec.Command("ssh",
"-o", "StrictHostKeyChecking=no",
"-o", "UserKnownHostsFile=/dev/null",
"-i", sshKey,
fmt.Sprintf("%s@%s", sshUser, vm.IPAddress), command)
If the command is not an SCP transfer, it runs the command over SSH instead.
- This builds the SSH command dynamically, using the provided
sshUser
,sshKey
, andvm.IPAddress
. - The command is executed remotely on the target VM.
Capturing and Streaming Output
stdout, _ := cmd.StdoutPipe()
stderr, _ := cmd.StderrPipe()
if err := cmd.Start(); err != nil {
return fmt.Errorf("[ERROR] Failed to start command on %s: %v", vm.Name, err)
}
// Stream stdout
go func() {
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
fmt.Printf("[OUTPUT] %s\n", scanner.Text())
}
}()
// Stream stderr
go func() {
scanner := bufio.NewScanner(stderr)
for scanner.Scan() {
fmt.Printf("[ERROR] %s\n", scanner.Text())
}
}()
StdoutPipe()
andStderrPipe()
capture output streams separately.- The function then starts streaming stdout and stderr in real-time.
- Instead of waiting until the command finishes, output is displayed as it arrives.
- This provides immediate feedback, which is useful for debugging.
Waiting for Command Completion
err := cmd.Wait()
if err != nil {
fmt.Printf("[ERROR] Command execution failed on %s: %v\n", vm.Name, err)
return err
}
- The function waits for the SSH or SCP command to complete.
- If an error occurs, it is logged, and the function returns an error.
Why This Matters
- This function allows fully automated remote execution of provisioning tasks.
- It supports both command execution and file transfers, covering all provisioning needs.
- Real-time output streaming makes it easier to debug deployment issues.
- It ensures that every server in CodexMCP can be configured remotely, reducing manual effort.
This function is a key component of CodexMCP’s automation stack, ensuring that infrastructure can be provisioned and configured dynamically across multiple nodes.