2024-10-29 01:35:59 -04:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2024-10-31 14:49:15 -04:00
|
|
|
"github.com/chzyer/readline"
|
2024-10-29 01:35:59 -04:00
|
|
|
"github.com/docker/docker/api/types/container"
|
|
|
|
"github.com/docker/docker/client"
|
2024-10-29 21:51:29 -04:00
|
|
|
"go-i2p-testnet/lib/docker_control"
|
2024-10-31 13:12:20 -04:00
|
|
|
goi2p "go-i2p-testnet/lib/go-i2p"
|
2024-10-31 20:04:05 -04:00
|
|
|
"go-i2p-testnet/lib/utils/logger"
|
2024-10-29 15:54:43 -04:00
|
|
|
"os"
|
|
|
|
"os/signal"
|
2024-10-31 14:49:15 -04:00
|
|
|
"strings"
|
2024-10-29 15:54:43 -04:00
|
|
|
"sync"
|
|
|
|
"syscall"
|
2024-10-29 01:35:59 -04:00
|
|
|
)
|
|
|
|
|
2024-10-31 16:09:59 -04:00
|
|
|
var (
|
|
|
|
running = false
|
|
|
|
// Track created containers and volumes for cleanup
|
2024-10-31 18:14:44 -04:00
|
|
|
createdGOI2Prouters []string
|
|
|
|
createdContainers []string
|
|
|
|
createdVolumes []string
|
|
|
|
mu sync.Mutex // To protect access to the slices
|
2024-10-31 20:04:05 -04:00
|
|
|
log = logger.GetTestnetLogger()
|
2024-10-31 16:09:59 -04:00
|
|
|
)
|
|
|
|
|
2024-10-31 19:35:44 -04:00
|
|
|
const (
|
|
|
|
NETWORK = "go-i2p-testnet"
|
|
|
|
)
|
2024-10-31 16:09:59 -04:00
|
|
|
|
2024-10-29 15:54:43 -04:00
|
|
|
// cleanup removes all created Docker resources: containers, volumes, and network.
|
|
|
|
func cleanup(cli *client.Client, ctx context.Context, createdContainers []string, createdVolumes []string, networkName string) {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithField("networkName", networkName).Debug("Starting cleanup of Docker resources")
|
2024-10-29 15:54:43 -04:00
|
|
|
|
|
|
|
// Remove containers
|
|
|
|
for _, containerID := range createdContainers {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithField("containerID", containerID).Debug("Attempting to stop container")
|
2024-10-29 15:54:43 -04:00
|
|
|
// Attempt to stop the container
|
|
|
|
timeout := 10 // seconds
|
|
|
|
err := cli.ContainerStop(ctx, containerID, container.StopOptions{Timeout: &timeout})
|
|
|
|
if err != nil {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithFields(map[string]interface{}{
|
|
|
|
"containerID": containerID,
|
|
|
|
"error": err,
|
|
|
|
}).Error("Failed to stop container")
|
2024-10-29 15:54:43 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Attempt to remove the container
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithField("containerID", containerID).Debug("Attempting to remove container")
|
2024-10-29 15:54:43 -04:00
|
|
|
removeOptions := container.RemoveOptions{Force: true}
|
|
|
|
err = cli.ContainerRemove(ctx, containerID, removeOptions)
|
|
|
|
if err != nil {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithFields(map[string]interface{}{
|
|
|
|
"containerID": containerID,
|
|
|
|
"error": err,
|
|
|
|
}).Error("Failed to remove container")
|
2024-10-29 15:54:43 -04:00
|
|
|
} else {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithField("containerID", containerID).Debug("Successfully removed container")
|
2024-10-29 15:54:43 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove volumes
|
|
|
|
for _, volumeName := range createdVolumes {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithField("volumeName", volumeName).Debug("Attempting to remove volume")
|
2024-10-29 15:54:43 -04:00
|
|
|
err := cli.VolumeRemove(ctx, volumeName, true)
|
|
|
|
if err != nil {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithFields(map[string]interface{}{
|
|
|
|
"volumeName": volumeName,
|
|
|
|
"error": err,
|
|
|
|
}).Error("Failed to remove volume")
|
2024-10-29 15:54:43 -04:00
|
|
|
} else {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithField("volumeName", volumeName).Debug("Successfully removed volume")
|
2024-10-29 15:54:43 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove network
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithField("networkName", networkName).Debug("Attempting to remove network")
|
2024-10-29 15:54:43 -04:00
|
|
|
err := cli.NetworkRemove(ctx, networkName)
|
|
|
|
if err != nil {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithFields(map[string]interface{}{
|
|
|
|
"networkName": networkName,
|
|
|
|
"error": err,
|
|
|
|
}).Error("Failed to remove network")
|
2024-10-29 15:54:43 -04:00
|
|
|
} else {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithField("networkName", networkName).Debug("Successfully removed network")
|
2024-10-29 15:54:43 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-31 15:38:33 -04:00
|
|
|
func addCreated(containerID, volumeID string) {
|
2024-10-31 21:13:36 -04:00
|
|
|
//mu.Lock() //For some reason, this freezes
|
|
|
|
//defer mu.Unlock()
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithFields(map[string]interface{}{
|
|
|
|
"containerID": containerID,
|
|
|
|
"volumeID": volumeID,
|
|
|
|
}).Debug("Tracking new container and volume")
|
2024-10-31 15:38:33 -04:00
|
|
|
createdContainers = append(createdContainers, containerID)
|
|
|
|
createdVolumes = append(createdVolumes, volumeID)
|
2024-10-31 21:13:36 -04:00
|
|
|
return
|
2024-10-31 15:38:33 -04:00
|
|
|
}
|
2024-10-29 01:35:59 -04:00
|
|
|
|
2024-10-31 15:38:33 -04:00
|
|
|
func start(cli *client.Client, ctx context.Context) {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.Debug("Starting testnet initialization")
|
2024-10-29 01:35:59 -04:00
|
|
|
// Create Docker network
|
2024-10-31 16:09:59 -04:00
|
|
|
networkName := NETWORK
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithField("networkName", networkName).Debug("Creating Docker network")
|
2024-10-31 13:11:10 -04:00
|
|
|
networkID, err := docker_control.CreateDockerNetwork(cli, ctx, networkName)
|
2024-10-29 01:35:59 -04:00
|
|
|
if err != nil {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithError(err).Fatal("Failed to create Docker network")
|
|
|
|
//log.Fatalf("Error creating Docker network: %v", err)
|
2024-10-29 01:35:59 -04:00
|
|
|
}
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithFields(map[string]interface{}{
|
|
|
|
"networkName": networkName,
|
|
|
|
"networkID": networkID,
|
|
|
|
}).Debug("Successfully created network")
|
2024-10-31 19:35:44 -04:00
|
|
|
|
|
|
|
//Create shared volume
|
2024-10-31 20:44:59 -04:00
|
|
|
log.Debug("Creating shared volume")
|
2024-10-31 19:35:44 -04:00
|
|
|
sharedVolumeName, err := docker_control.CreateSharedVolume(cli, ctx)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("error creating shared volume: %v", err)
|
|
|
|
}
|
|
|
|
createdVolumes = append(createdVolumes, sharedVolumeName)
|
|
|
|
running = true
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithField("volumeName", sharedVolumeName).Debug("Successfully created shared volume")
|
2024-10-31 18:14:44 -04:00
|
|
|
/*
|
|
|
|
// Number of routers to create
|
|
|
|
numRouters := 3
|
|
|
|
|
|
|
|
// IP addresses for the routers
|
|
|
|
baseIP := "172.28.0."
|
|
|
|
routerIPs := make([]string, numRouters)
|
|
|
|
for i := 0; i < numRouters; i++ {
|
|
|
|
routerIPs[i] = fmt.Sprintf("%s%d", baseIP, i+2) // Starting from .2
|
|
|
|
}
|
2024-10-29 01:35:59 -04:00
|
|
|
|
2024-10-31 18:14:44 -04:00
|
|
|
// Spin up routers
|
|
|
|
for i := 0; i < numRouters; i++ {
|
|
|
|
routerID := i + 1
|
|
|
|
ip := routerIPs[i]
|
2024-10-29 01:35:59 -04:00
|
|
|
|
2024-10-31 18:14:44 -04:00
|
|
|
// Collect peers (other router IPs)
|
|
|
|
peers := make([]string, 0)
|
|
|
|
for j, peerIP := range routerIPs {
|
|
|
|
if j != i {
|
|
|
|
peers = append(peers, peerIP)
|
|
|
|
}
|
2024-10-29 01:35:59 -04:00
|
|
|
}
|
|
|
|
|
2024-10-31 18:14:44 -04:00
|
|
|
// Generate router configuration
|
|
|
|
configData := goi2p.GenerateRouterConfig(routerID)
|
2024-10-29 01:35:59 -04:00
|
|
|
|
2024-10-31 18:14:44 -04:00
|
|
|
// Create the container
|
|
|
|
containerID, volumeName, err := goi2p.CreateRouterContainer(cli, ctx, routerID, ip, networkName, configData)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("Error creating router container: %v", err)
|
|
|
|
}
|
|
|
|
fmt.Printf("Started router%d with IP %s\n", routerID, ip)
|
|
|
|
|
|
|
|
// Track the created container and volume for cleanup
|
|
|
|
addCreated(containerID, volumeName)
|
2024-10-29 01:35:59 -04:00
|
|
|
}
|
2024-10-29 15:54:43 -04:00
|
|
|
|
2024-10-31 18:14:44 -04:00
|
|
|
*/
|
2024-10-31 16:17:12 -04:00
|
|
|
|
2024-10-31 16:09:59 -04:00
|
|
|
}
|
2024-10-31 15:38:33 -04:00
|
|
|
|
2024-10-31 19:35:44 -04:00
|
|
|
func addGOI2PRouter(cli *client.Client, ctx context.Context) error {
|
2024-10-31 18:14:44 -04:00
|
|
|
mu.Lock()
|
2024-10-31 21:13:36 -04:00
|
|
|
defer mu.Unlock()
|
2024-10-31 18:14:44 -04:00
|
|
|
routerID := len(createdGOI2Prouters) + 1
|
|
|
|
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithField("routerID", routerID).Debug("Adding new GO-I2P router")
|
|
|
|
|
2024-10-31 18:14:44 -04:00
|
|
|
// Calculate next IP
|
|
|
|
incr := routerID + 1
|
|
|
|
if incr == 256 {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.Error("Maximum number of nodes reached (255)")
|
|
|
|
return fmt.Errorf("too many nodes! (255)")
|
2024-10-31 18:14:44 -04:00
|
|
|
}
|
|
|
|
nextIP := fmt.Sprintf("172.28.0.%d", incr)
|
|
|
|
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithFields(map[string]interface{}{
|
|
|
|
"routerID": routerID,
|
|
|
|
"ip": nextIP,
|
|
|
|
}).Debug("Generating router configuration")
|
|
|
|
|
2024-10-31 18:14:44 -04:00
|
|
|
configData := goi2p.GenerateRouterConfig(routerID)
|
|
|
|
|
|
|
|
// Create the container
|
2024-10-31 20:44:59 -04:00
|
|
|
log.Debug("Creating router container")
|
2024-10-31 19:35:44 -04:00
|
|
|
containerID, volumeID, err := goi2p.CreateRouterContainer(cli, ctx, routerID, nextIP, NETWORK, configData)
|
2024-10-31 18:14:44 -04:00
|
|
|
if err != nil {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithError(err).Error("Failed to create router container")
|
2024-10-31 18:14:44 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithFields(map[string]interface{}{
|
|
|
|
"routerID": routerID,
|
|
|
|
"containerID": containerID,
|
|
|
|
"volumeID": volumeID,
|
|
|
|
"ip": nextIP,
|
|
|
|
}).Debug("Adding router to tracking lists")
|
2024-10-31 18:14:44 -04:00
|
|
|
createdGOI2Prouters = append(createdGOI2Prouters, containerID)
|
|
|
|
createdContainers = append(createdContainers, containerID)
|
2024-10-31 19:35:44 -04:00
|
|
|
createdVolumes = append(createdVolumes, volumeID)
|
|
|
|
|
|
|
|
addCreated(containerID, volumeID)
|
2024-10-31 18:14:44 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-10-31 15:38:33 -04:00
|
|
|
func main() {
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
// Initialize Docker client
|
2024-10-31 20:44:59 -04:00
|
|
|
log.Debug("Initializing Docker client")
|
2024-10-31 15:38:33 -04:00
|
|
|
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
|
|
|
if err != nil {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithError(err).Fatal("Failed to create Docker client")
|
2024-10-31 15:38:33 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure cleanup is performed on exit
|
|
|
|
defer func() {
|
|
|
|
if running {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.Debug("Performing cleanup on exit")
|
2024-10-31 16:09:59 -04:00
|
|
|
cleanup(cli, ctx, createdContainers, createdVolumes, NETWORK)
|
2024-10-31 15:38:33 -04:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Set up signal handling to gracefully handle termination
|
|
|
|
sigs := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
|
2024-10-31 14:49:15 -04:00
|
|
|
//Begin command loop
|
|
|
|
// Setup readline for command line input
|
2024-10-31 20:44:59 -04:00
|
|
|
log.Debug("Initializing readline interface")
|
2024-10-31 14:49:15 -04:00
|
|
|
rl, err := readline.New("> ")
|
|
|
|
if err != nil {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithError(err).Fatal("Failed to initialize readline")
|
2024-10-31 14:49:15 -04:00
|
|
|
}
|
|
|
|
defer rl.Close()
|
2024-10-31 20:44:59 -04:00
|
|
|
log.Debug("Starting command loop")
|
2024-10-31 14:49:15 -04:00
|
|
|
for {
|
|
|
|
line, err := rl.Readline()
|
|
|
|
if err != nil { //EOF
|
|
|
|
break
|
|
|
|
}
|
|
|
|
line = strings.TrimSpace(line)
|
|
|
|
parts := strings.Fields(line)
|
|
|
|
if len(parts) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithField("command", parts[0]).Debug("Processing command")
|
|
|
|
|
2024-10-31 14:49:15 -04:00
|
|
|
// handle commands
|
|
|
|
switch parts[0] {
|
|
|
|
case "help":
|
|
|
|
showHelp()
|
2024-10-31 15:38:33 -04:00
|
|
|
case "start":
|
|
|
|
if running {
|
|
|
|
fmt.Println("Testnet is already running")
|
|
|
|
} else {
|
|
|
|
start(cli, ctx)
|
|
|
|
}
|
|
|
|
case "stop":
|
|
|
|
if running {
|
2024-10-31 16:09:59 -04:00
|
|
|
cleanup(cli, ctx, createdContainers, createdVolumes, NETWORK)
|
2024-10-31 15:38:33 -04:00
|
|
|
running = false
|
|
|
|
} else {
|
|
|
|
fmt.Println("Testnet isn't running")
|
|
|
|
}
|
2024-10-31 20:04:05 -04:00
|
|
|
case "build":
|
|
|
|
if running {
|
|
|
|
fmt.Println("Testnet is running, not safe to build")
|
|
|
|
} else {
|
|
|
|
err := buildImages(cli, ctx)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("failed to build images: %v\n", err)
|
|
|
|
}
|
|
|
|
}
|
2024-10-31 16:09:59 -04:00
|
|
|
case "rebuild":
|
|
|
|
if running {
|
|
|
|
fmt.Println("Testnet is running, not safe to rebuild")
|
|
|
|
} else {
|
2024-10-31 19:41:41 -04:00
|
|
|
err := rebuildImages(cli, ctx)
|
2024-10-31 16:09:59 -04:00
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("failed to rebuild images: %v\n", err)
|
|
|
|
}
|
|
|
|
}
|
2024-10-31 16:17:12 -04:00
|
|
|
case "remove_images":
|
|
|
|
if running {
|
|
|
|
fmt.Println("Testnet is running, not safe to remove images")
|
|
|
|
} else {
|
2024-10-31 19:41:41 -04:00
|
|
|
err := removeImages(cli, ctx)
|
2024-10-31 16:17:12 -04:00
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("failed to remove images: %v\n", err)
|
|
|
|
}
|
|
|
|
}
|
2024-10-31 18:14:44 -04:00
|
|
|
case "add_goi2p_router":
|
|
|
|
if !running {
|
|
|
|
fmt.Println("Testnet isn't running")
|
|
|
|
} else {
|
2024-10-31 19:35:44 -04:00
|
|
|
err := addGOI2PRouter(cli, ctx)
|
2024-10-31 18:14:44 -04:00
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("failed to add router: %v\n", err)
|
|
|
|
}
|
|
|
|
}
|
2024-10-31 14:49:15 -04:00
|
|
|
case "exit":
|
2024-10-31 15:38:33 -04:00
|
|
|
fmt.Println("Exiting...")
|
|
|
|
if running {
|
2024-10-31 16:09:59 -04:00
|
|
|
cleanup(cli, ctx, createdContainers, createdVolumes, NETWORK)
|
2024-10-31 15:38:33 -04:00
|
|
|
}
|
2024-10-31 14:49:15 -04:00
|
|
|
return
|
|
|
|
default:
|
|
|
|
fmt.Println("Unknown command. Type 'help' for a list of commands")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-29 15:54:43 -04:00
|
|
|
// Wait for interrupt signal to gracefully shutdown
|
|
|
|
<-sigs
|
|
|
|
fmt.Println("\nReceived interrupt signal. Initiating cleanup...")
|
2024-10-29 01:35:59 -04:00
|
|
|
}
|
2024-10-31 14:49:15 -04:00
|
|
|
|
|
|
|
func showHelp() {
|
|
|
|
fmt.Println("Available commands:")
|
2024-10-31 18:14:44 -04:00
|
|
|
fmt.Println(" help - Show this help message")
|
|
|
|
fmt.Println(" start - Start routers")
|
|
|
|
fmt.Println(" stop - Stop and cleanup routers")
|
2024-10-31 20:04:05 -04:00
|
|
|
fmt.Println(" build - Build docker images for nodes")
|
2024-10-31 18:14:44 -04:00
|
|
|
fmt.Println(" rebuild - Rebuild docker images for nodes")
|
|
|
|
fmt.Println(" remove_images - Removes all node images")
|
|
|
|
fmt.Println(" add_goi2p_router - Add a router node (go-i2p)")
|
|
|
|
fmt.Println(" exit - Exit the CLI")
|
2024-10-31 14:49:15 -04:00
|
|
|
}
|
2024-10-31 19:41:41 -04:00
|
|
|
|
|
|
|
func buildImages(cli *client.Client, ctx context.Context) error {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.Debug("Building GO-I2P node image")
|
2024-10-31 19:41:41 -04:00
|
|
|
err := goi2p.BuildImage(cli, ctx)
|
|
|
|
if err != nil {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithError(err).Error("Failed to build GO-I2P node image")
|
2024-10-31 19:41:41 -04:00
|
|
|
return err
|
|
|
|
}
|
2024-10-31 20:44:59 -04:00
|
|
|
log.Debug("Successfully built GO-I2P node image")
|
2024-10-31 19:41:41 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func removeImages(cli *client.Client, ctx context.Context) error {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.Debug("Removing GO-I2P node image")
|
2024-10-31 19:41:41 -04:00
|
|
|
err := goi2p.RemoveImage(cli, ctx)
|
|
|
|
if err != nil {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithError(err).Error("Failed to remove GO-I2P node image")
|
2024-10-31 19:41:41 -04:00
|
|
|
return err
|
|
|
|
}
|
2024-10-31 20:44:59 -04:00
|
|
|
log.Debug("Successfully removed GO-I2P node image")
|
2024-10-31 19:41:41 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func rebuildImages(cli *client.Client, ctx context.Context) error {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.Debug("Starting image rebuild process")
|
2024-10-31 19:41:41 -04:00
|
|
|
err := removeImages(cli, ctx)
|
|
|
|
if err != nil {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithError(err).Error("Failed during image removal step of rebuild")
|
2024-10-31 19:41:41 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = buildImages(cli, ctx)
|
|
|
|
if err != nil {
|
2024-10-31 20:44:59 -04:00
|
|
|
log.WithError(err).Error("Failed during image build step of rebuild")
|
2024-10-31 19:41:41 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-10-31 20:44:59 -04:00
|
|
|
log.Debug("Successfully completed image rebuild")
|
2024-10-31 19:41:41 -04:00
|
|
|
return nil
|
|
|
|
}
|