This commit is contained in:
2025-05-23 16:10:49 +04:00
parent 3f01074e3f
commit 29d0d25a3b
133 changed files with 346 additions and 168 deletions

View File

@@ -0,0 +1,19 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
vmhandler
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work

View File

@@ -0,0 +1,134 @@
# VM Handler Example
This example demonstrates how to use the HandlerFactory with a VM handler to process heroscript commands.
## Overview
The VM handler example shows how to:
1. Create a handler that processes VM-related actions
2. Register the handler with the HandlerFactory
3. Start a telnet server that uses the HandlerFactory to process commands
4. Connect to the telnet server and send heroscript commands
## Running the Example
To run the example:
```bash
cd ~/code/github/freeflowuniverse/herocode/heroagent/pkg/handlerfactory/cmd/vmhandler
go run . tutorial
#to run just the server do
go run .
#you can then go to other terminal and play with telnet / nc
```
This will start a telnet server on:
- Unix socket: `/tmp/vmhandler.sock`
- TCP: `localhost:8024`
## Connecting to the Server
### Using Unix Socket
```bash
nc -U /tmp/vmhandler.sock
```
### Using TCP
```bash
telnet localhost 8024
```
## Authentication
When you connect, you'll need to authenticate with the secret:
```
!!auth secret:1234
```
## Available Commands
Once authenticated, you can use the following commands:
```
!!vm.define name:'test_vm' cpu:4 memory:'8GB' storage:'100GB'
!!vm.start name:'test_vm'
!!vm.stop name:'test_vm'
!!vm.disk_add name:'test_vm' size:'50GB' type:'SSD'
!!vm.list
!!vm.status name:'test_vm'
!!vm.delete name:'test_vm' force:true
```
## Example Session
Here's an example session:
```
$ telnet localhost 8024
Connected to localhost.
Escape character is '^]'.
** Welcome: you are not authenticated, provide secret.
!!auth secret:1234
** Welcome: you are authenticated.
!!vm.define name:'test_vm' cpu:4 memory:'8GB' storage:'100GB'
VM 'test_vm' defined successfully with 4 CPU, 8GB memory, and 100GB storage
!!vm.start name:'test_vm'
VM 'test_vm' started successfully
!!vm.disk_add name:'test_vm' size:'50GB' type:'SSD'
Added 50GB SSD disk to VM 'test_vm'
!!vm.status name:'test_vm'
VM 'test_vm' status:
- Status: running
- CPU: 4
- Memory: 8GB
- Storage: 100GB
- Attached disks:
1. 50GB SSD
!!vm.list
Defined VMs:
- test_vm (running): 4 CPU, 8GB memory, 100GB storage
Attached disks:
1. 50GB SSD
!!vm.stop name:'test_vm'
VM 'test_vm' stopped successfully
!!vm.delete name:'test_vm'
VM 'test_vm' deleted successfully
!!quit
Goodbye!
Connection closed by foreign host.
```
## Other Commands
- `!!help`, `h`, or `?` - Show help
- `!!interactive` or `!!i` - Toggle interactive mode (with colors)
- `!!quit`, `!!exit`, or `q` - Disconnect from server
## How It Works
1. The `main.go` file creates a HandlerFactory and registers the VM handler
2. It starts a telnet server that uses the HandlerFactory to process commands
3. When a client connects and sends a heroscript command, the server:
- Parses the command to determine the actor and action
- Calls the appropriate method on the VM handler
- Returns the result to the client
## Extending the Example
You can extend this example by:
1. Adding more methods to the VM handler
2. Creating new handlers for different actors
3. Registering multiple handlers with the HandlerFactory

View File

@@ -0,0 +1,276 @@
package main
import (
"bufio"
"fmt"
"os"
"time"
"git.ourworld.tf/herocode/heroagent/pkg/handlerfactory"
)
// runTutorial runs an interactive tutorial demonstrating the VM handler
func runTutorial() {
fmt.Println("=== VM Handler Tutorial ===")
fmt.Println("This tutorial will demonstrate how to use the VM handler with heroscript commands.")
fmt.Println("Press Enter to continue through each step...")
waitForEnter()
// Create a new handler factory
fmt.Println("\nStep 1: Create a new HandlerFactory")
fmt.Println("factory := handlerfactory.NewHandlerFactory()")
factory := handlerfactory.NewHandlerFactory()
waitForEnter()
// Create a VM handler
fmt.Println("\nStep 2: Create a VM handler")
fmt.Println("vmHandler := NewVMHandler()")
vmHandler := NewVMHandler()
waitForEnter()
// Register the VM handler with the factory
fmt.Println("\nStep 3: Register the VM handler with the factory")
fmt.Println("factory.RegisterHandler(vmHandler)")
err := factory.RegisterHandler(vmHandler)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Println("Handler registered successfully!")
waitForEnter()
// Show available actions
fmt.Println("\nStep 4: List available actions for the VM handler")
actions := factory.GetSupportedActions()
fmt.Println("Supported actions for 'vm' actor:")
for _, action := range actions["vm"] {
fmt.Printf("- %s\n", action)
}
waitForEnter()
// Process heroscript commands
fmt.Println("\nStep 5: Process heroscript commands")
// Define a VM
defineScript := `!!vm.define name:'tutorial_vm' cpu:2 memory:'4GB' storage:'50GB'
description: 'A tutorial VM for demonstration purposes'`
fmt.Println("\nCommand:")
fmt.Println(defineScript)
fmt.Println("\nProcessing...")
time.Sleep(1 * time.Second)
result, err := factory.ProcessHeroscript(defineScript)
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Printf("Result: %s\n", result)
}
waitForEnter()
// Start the VM
startScript := `!!vm.start name:'tutorial_vm'`
fmt.Println("\nCommand:")
fmt.Println(startScript)
fmt.Println("\nProcessing...")
time.Sleep(1 * time.Second)
result, err = factory.ProcessHeroscript(startScript)
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Printf("Result: %s\n", result)
}
waitForEnter()
// Add a disk
diskAddScript := `!!vm.disk_add name:'tutorial_vm' size:'20GB' type:'SSD'`
fmt.Println("\nCommand:")
fmt.Println(diskAddScript)
fmt.Println("\nProcessing...")
time.Sleep(1 * time.Second)
result, err = factory.ProcessHeroscript(diskAddScript)
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Printf("Result: %s\n", result)
}
waitForEnter()
// Check VM status
statusScript := `!!vm.status name:'tutorial_vm'`
fmt.Println("\nCommand:")
fmt.Println(statusScript)
fmt.Println("\nProcessing...")
time.Sleep(1 * time.Second)
result, err = factory.ProcessHeroscript(statusScript)
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Printf("Result: %s\n", result)
}
waitForEnter()
// List all VMs
listScript := `!!vm.list`
fmt.Println("\nCommand:")
fmt.Println(listScript)
fmt.Println("\nProcessing...")
time.Sleep(1 * time.Second)
result, err = factory.ProcessHeroscript(listScript)
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Printf("Result: %s\n", result)
}
waitForEnter()
// Stop the VM
stopScript := `!!vm.stop name:'tutorial_vm'`
fmt.Println("\nCommand:")
fmt.Println(stopScript)
fmt.Println("\nProcessing...")
time.Sleep(1 * time.Second)
result, err = factory.ProcessHeroscript(stopScript)
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Printf("Result: %s\n", result)
}
waitForEnter()
// Delete the VM
deleteScript := `!!vm.delete name:'tutorial_vm'`
fmt.Println("\nCommand:")
fmt.Println(deleteScript)
fmt.Println("\nProcessing...")
time.Sleep(1 * time.Second)
result, err = factory.ProcessHeroscript(deleteScript)
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Printf("Result: %s\n", result)
}
waitForEnter()
// Try an invalid command
invalidScript := `!!vm.invalid name:'tutorial_vm'`
fmt.Println("\nInvalid Command:")
fmt.Println(invalidScript)
fmt.Println("\nProcessing...")
time.Sleep(1 * time.Second)
result, err = factory.ProcessHeroscript(invalidScript)
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Printf("Result: %s\n", result)
}
waitForEnter()
// Conclusion
fmt.Println("\nTutorial Complete!")
fmt.Println("You've seen how to:")
fmt.Println("1. Create a HandlerFactory")
fmt.Println("2. Register a VM handler")
fmt.Println("3. Process various heroscript commands")
fmt.Println("\nTo run the full telnet server example, execute:")
fmt.Println("go run main.go vm_handler.go")
fmt.Println("\nPress Enter to exit the tutorial...")
waitForEnter()
}
// waitForEnter waits for the user to press Enter
func waitForEnter() {
reader := bufio.NewReader(os.Stdin)
reader.ReadString('\n')
}
// VMTutorial contains the tutorial text for the VM handler
const VMTutorial = `
VM Handler Tutorial
==================
The VM handler provides a set of commands to manage virtual machines through heroscript commands.
Available VM commands:
!!vm.define name:test_vm cpu:4 memory:8GB storage:100GB
!!vm.start name:test_vm
!!vm.stop name:test_vm
!!vm.disk_add name:test_vm size:50GB type:SSD
!!vm.list
!!vm.status name:test_vm
!!vm.delete name:test_vm force:true
Authentication secret: 1234
Command Details:
--------------
1. define - Create a new VM with specified resources
Parameters:
- name: (required) Name of the VM
- cpu: (optional) Number of CPUs, default: 1
- memory: (optional) Memory size, default: 1GB
- storage: (optional) Storage size, default: 10GB
- description: (optional) Description of the VM
2. start - Start a VM
Parameters:
- name: (required) Name of the VM to start
3. stop - Stop a running VM
Parameters:
- name: (required) Name of the VM to stop
4. disk_add - Add a disk to a VM
Parameters:
- name: (required) Name of the VM
- size: (optional) Size of the disk, default: 10GB
- type: (optional) Type of disk (SSD, HDD), default: HDD
5. list - List all VMs
6. status - Show status of a VM
Parameters:
- name: (required) Name of the VM
7. delete - Delete a VM
Parameters:
- name: (required) Name of the VM
- force: (optional) Force deletion even if VM is running, default: false
8. help - Show this help message
Examples:
--------
1. Create a new VM:
!!vm.define name:webserver cpu:2 memory:4GB storage:50GB description:'Web server VM'
2. Start the VM:
!!vm.start name:webserver
3. Add an SSD disk:
!!vm.disk_add name:webserver size:100GB type:SSD
4. Check VM status:
!!vm.status name:webserver
5. List all VMs:
!!vm.list
6. Stop the VM:
!!vm.stop name:webserver
7. Delete the VM:
!!vm.delete name:webserver force:true
`
// addTutorialCommand adds the tutorial command to the main function
func addTutorialCommand() {
// Check command line arguments
if len(os.Args) > 1 && os.Args[1] == "tutorial" {
runTutorial()
os.Exit(0)
}
}
// GetVMTutorial returns the VM handler tutorial text
func GetVMTutorial() string {
return VMTutorial
}

View File

@@ -0,0 +1,285 @@
package main
import (
"fmt"
"strings"
"git.ourworld.tf/herocode/heroagent/pkg/handlerfactory"
)
// VMHandler handles VM-related actions
type VMHandler struct {
handlerfactory.BaseHandler
vms map[string]*VM
}
// VM represents a virtual machine
type VM struct {
Name string
CPU int
Memory string
Storage string
Description string
Running bool
Disks []Disk
}
// Disk represents a disk attached to a VM
type Disk struct {
Size string
Type string
}
// NewVMHandler creates a new VM handler
func NewVMHandler() *VMHandler {
return &VMHandler{
BaseHandler: handlerfactory.BaseHandler{
ActorName: "vm",
},
vms: make(map[string]*VM),
}
}
// Define handles the vm.define action
func (h *VMHandler) Define(script string) string {
params, err := h.ParseParams(script)
if err != nil {
return fmt.Sprintf("Error parsing parameters: %v", err)
}
name := params.Get("name")
if name == "" {
return "Error: VM name is required"
}
// Check if VM already exists
if _, exists := h.vms[name]; exists {
return fmt.Sprintf("Error: VM '%s' already exists", name)
}
// Create new VM
cpu := params.GetIntDefault("cpu", 1)
memory := params.Get("memory")
if memory == "" {
memory = "1GB"
}
storage := params.Get("storage")
if storage == "" {
storage = "10GB"
}
description := params.Get("description")
vm := &VM{
Name: name,
CPU: cpu,
Memory: memory,
Storage: storage,
Description: description,
Running: false,
Disks: []Disk{},
}
// Add VM to map
h.vms[name] = vm
return fmt.Sprintf("VM '%s' defined successfully with %d CPU, %s memory, and %s storage",
name, cpu, memory, storage)
}
// Start handles the vm.start action
func (h *VMHandler) Start(script string) string {
params, err := h.ParseParams(script)
if err != nil {
return fmt.Sprintf("Error parsing parameters: %v", err)
}
name := params.Get("name")
if name == "" {
return "Error: VM name is required"
}
// Find VM
vm, exists := h.vms[name]
if !exists {
return fmt.Sprintf("Error: VM '%s' not found", name)
}
// Start VM
if vm.Running {
return fmt.Sprintf("VM '%s' is already running", name)
}
vm.Running = true
return fmt.Sprintf("VM '%s' started successfully", name)
}
// Stop handles the vm.stop action
func (h *VMHandler) Stop(script string) string {
params, err := h.ParseParams(script)
if err != nil {
return fmt.Sprintf("Error parsing parameters: %v", err)
}
name := params.Get("name")
if name == "" {
return "Error: VM name is required"
}
// Find VM
vm, exists := h.vms[name]
if !exists {
return fmt.Sprintf("Error: VM '%s' not found", name)
}
// Stop VM
if !vm.Running {
return fmt.Sprintf("VM '%s' is already stopped", name)
}
vm.Running = false
return fmt.Sprintf("VM '%s' stopped successfully", name)
}
// DiskAdd handles the vm.disk_add action
func (h *VMHandler) DiskAdd(script string) string {
params, err := h.ParseParams(script)
if err != nil {
return fmt.Sprintf("Error parsing parameters: %v", err)
}
name := params.Get("name")
if name == "" {
return "Error: VM name is required"
}
// Find VM
vm, exists := h.vms[name]
if !exists {
return fmt.Sprintf("Error: VM '%s' not found", name)
}
// Add disk
size := params.Get("size")
if size == "" {
size = "10GB"
}
diskType := params.Get("type")
if diskType == "" {
diskType = "HDD"
}
disk := Disk{
Size: size,
Type: diskType,
}
vm.Disks = append(vm.Disks, disk)
return fmt.Sprintf("Added %s %s disk to VM '%s'", size, diskType, name)
}
// Delete handles the vm.delete action
func (h *VMHandler) Delete(script string) string {
params, err := h.ParseParams(script)
if err != nil {
return fmt.Sprintf("Error parsing parameters: %v", err)
}
name := params.Get("name")
if name == "" {
return "Error: VM name is required"
}
// Find VM
vm, exists := h.vms[name]
if !exists {
return fmt.Sprintf("Error: VM '%s' not found", name)
}
// Check if VM is running and force flag is not set
if vm.Running && !params.GetBool("force") {
return fmt.Sprintf("Error: VM '%s' is running. Use force:true to delete anyway", name)
}
// Delete VM
delete(h.vms, name)
return fmt.Sprintf("VM '%s' deleted successfully", name)
}
// List handles the vm.list action
func (h *VMHandler) List(script string) string {
if len(h.vms) == 0 {
return "No VMs defined"
}
var result strings.Builder
result.WriteString("Defined VMs:\n")
for _, vm := range h.vms {
status := "stopped"
if vm.Running {
status = "running"
}
result.WriteString(fmt.Sprintf("- %s (%s): %d CPU, %s memory, %s storage\n",
vm.Name, status, vm.CPU, vm.Memory, vm.Storage))
if len(vm.Disks) > 0 {
result.WriteString(" Attached disks:\n")
for i, disk := range vm.Disks {
result.WriteString(fmt.Sprintf(" %d. %s %s\n", i+1, disk.Size, disk.Type))
}
}
}
return result.String()
}
// Help handles the vm.help action
func (h *VMHandler) Help(script string) string {
return GetVMTutorial()
}
// Status handles the vm.status action
func (h *VMHandler) Status(script string) string {
params, err := h.ParseParams(script)
if err != nil {
return fmt.Sprintf("Error parsing parameters: %v", err)
}
name := params.Get("name")
if name == "" {
return "Error: VM name is required"
}
// Find VM
vm, exists := h.vms[name]
if !exists {
return fmt.Sprintf("Error: VM '%s' not found", name)
}
// Return VM status
status := "stopped"
if vm.Running {
status = "running"
}
var result strings.Builder
result.WriteString(fmt.Sprintf("VM '%s' status:\n", name))
result.WriteString(fmt.Sprintf("- Status: %s\n", status))
result.WriteString(fmt.Sprintf("- CPU: %d\n", vm.CPU))
result.WriteString(fmt.Sprintf("- Memory: %s\n", vm.Memory))
result.WriteString(fmt.Sprintf("- Storage: %s\n", vm.Storage))
if vm.Description != "" {
result.WriteString(fmt.Sprintf("- Description: %s\n", vm.Description))
}
if len(vm.Disks) > 0 {
result.WriteString("- Attached disks:\n")
for i, disk := range vm.Disks {
result.WriteString(fmt.Sprintf(" %d. %s %s\n", i+1, disk.Size, disk.Type))
}
}
return result.String()
}

View File

@@ -0,0 +1,75 @@
package main
import (
"fmt"
"log"
"os"
"os/signal"
"path/filepath"
"syscall"
"git.ourworld.tf/herocode/heroagent/pkg/handlerfactory"
)
// The tutorial functions are defined in tutorial.go
func main() {
// Check if tutorial mode is requested
addTutorialCommand()
fmt.Println("Starting VM Handler Example")
// Create a new handler factory
factory := handlerfactory.NewHandlerFactory()
// Create and register the VM handler
vmHandler := NewVMHandler()
err := factory.RegisterHandler(vmHandler)
if err != nil {
log.Fatalf("Failed to register VM handler: %v", err)
}
// Create a telnet server with the handler factory
server := handlerfactory.NewTelnetServer(factory, "1234")
// Create socket directory if it doesn't exist
socketDir := "/tmp"
err = os.MkdirAll(socketDir, 0755)
if err != nil {
log.Fatalf("Failed to create socket directory: %v", err)
}
// Start the telnet server on a Unix socket
socketPath := filepath.Join(socketDir, "vmhandler.sock")
err = server.Start(socketPath)
if err != nil {
log.Fatalf("Failed to start telnet server: %v", err)
}
fmt.Printf("Telnet server started on socket: %s\n", socketPath)
fmt.Printf("Connect with: nc -U %s\n", socketPath)
// Also start on TCP port for easier access
err = server.StartTCP("localhost:8024")
if err != nil {
log.Fatalf("Failed to start TCP telnet server: %v", err)
}
fmt.Println("Telnet server started on TCP: localhost:8024")
fmt.Println("Connect with: telnet localhost 8024")
// Print available commands
fmt.Println("\nVM Handler started. Type '!!vm.help' to see available commands.")
fmt.Println("Authentication secret: 1234")
// Wait for interrupt signal
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
// Stop the server
fmt.Println("Stopping server...")
err = server.Stop()
if err != nil {
log.Fatalf("Failed to stop telnet server: %v", err)
}
fmt.Println("Telnet server stopped")
}