status-desktop/scripts/localfleet/main.go

294 lines
8.7 KiB
Go

package main
import (
"bytes"
"encoding/json"
"errors"
"flag"
"fmt"
"os"
"os/exec"
"os/signal"
"strings"
"syscall"
)
type Node struct {
enr string
name string
rpcPort int
}
type ClusterConfig struct {
Enabled bool
Fleet string
StaticNodes []string
BootNodes []string
TrustedMailServers []string
PushNotificationsServers []string
}
type Mailserver struct {
ID string
Address string
Fleet string
Version int
}
func main() {
// Only supports 1 boot node
mailserverNum := flag.Int("mailservers", 1, "number of mailservers")
whisperNum := flag.Int("whisper", 1, "number of whisper nodes")
flag.Parse()
fmt.Println("Starting a bootnode...")
bootnodeENR, err := startBootnode()
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Bootnode enr: ", bootnodeENR)
defer stopBootnode()
nodeCnt := 0
var mailservers []Node
for i := 0; i < *mailserverNum; i++ {
fmt.Println(fmt.Sprintf("Starting mailserver #%d...", i+1))
node, err := startNode(nodeCnt, bootnodeENR, true)
if err != nil {
stopNodes(mailservers)
fmt.Println("Could not start node", err)
return
}
fmt.Println(fmt.Sprintf("Mailserver #%d enr: %s", i+1, node.enr))
mailservers = append(mailservers, node)
nodeCnt++
}
var whisperNodes []Node
for i := 0; i < *whisperNum; i++ {
fmt.Println(fmt.Sprintf("Starting whisperNode #%d...", i+1))
node, err := startNode(nodeCnt, bootnodeENR, false)
if err != nil {
stopNodes(whisperNodes)
return
}
fmt.Println(fmt.Sprintf("Whisper #%d enr: %s", i+1, node.enr))
whisperNodes = append(whisperNodes, node)
nodeCnt++
}
for _, node := range append(mailservers, whisperNodes...) {
fmt.Println("Adding peers to ", node.name)
for _, peer := range append(mailservers, whisperNodes...) {
addPeer(peer.enr, node.rpcPort)
}
}
// Output config
cluster := ClusterConfig{
Enabled: true,
Fleet: "eth.prod",
BootNodes: []string{bootnodeENR},
}
for _, node := range mailservers {
cluster.TrustedMailServers = append(cluster.TrustedMailServers, node.enr)
}
for _, node := range whisperNodes {
cluster.StaticNodes = append(cluster.StaticNodes, node.enr)
}
clusterJSON, _ := json.Marshal(cluster)
fmt.Println("\nNew cluster config:\n", string(clusterJSON))
// ===============================================================================
// Replacing status-go fleets.go file
statusGoMailserverFleet := ""
mailserverStrTemplate := ` Mailserver {
ID: "%s",
Address: "%s",
Fleet: "eth.prod",
Version: 1,
},
`
for _, m := range mailservers {
statusGoMailserverFleet += fmt.Sprintf(mailserverStrTemplate, m.name, m.enr)
}
b, _ := os.ReadFile("./fleet.go.template")
statusGoMailserverFleet = strings.Replace(string(b), "%MAILSERVER_LIST%", statusGoMailserverFleet, -1)
err = os.WriteFile("../../vendor/status-go/services/mailservers/fleet.go", []byte(statusGoMailserverFleet), 0600)
if err != nil {
fmt.Println("Could not write fleet in status-go")
stopNodes(mailservers)
stopNodes(whisperNodes)
return
}
fmt.Println("\nvendor/status-go/services/mailservers/fleet.go was updated")
// =====================================================================================================
// Replacing status-desktop fleets.go file
b, _ = os.ReadFile("./fleets.json.template")
fleetsJSON := strings.Replace(string(b), "%BOOTNODE%", bootnodeENR, -1)
desktopMailserverFleet := ""
for _, m := range mailservers {
desktopMailserverFleet += fmt.Sprintf("\"%s\": \"%s\",", m.name, m.enr)
}
desktopMailserverFleet = strings.TrimSuffix(desktopMailserverFleet, ",")
fleetsJSON = strings.Replace(fleetsJSON, "%MAILSERVER_LIST%", desktopMailserverFleet, -1)
desktopWhisperFleet := ""
for _, m := range whisperNodes {
desktopWhisperFleet += fmt.Sprintf("\"%s\": \"%s\",", m.name, m.enr)
}
desktopWhisperFleet = strings.TrimSuffix(desktopWhisperFleet, ",")
fleetsJSON = strings.Replace(fleetsJSON, "%WHISPER_LIST%", desktopWhisperFleet, -1)
err = os.WriteFile("../../fleets.json", []byte(fleetsJSON), 0600)
if err != nil {
fmt.Println("Could not write fleet in status-desktop")
stopNodes(mailservers)
stopNodes(whisperNodes)
return
}
fmt.Println("fleets.json was updated")
fmt.Println("\nDONE! rebuild status-go and desktop to use this new fleet")
// Wait for a SIGINT or SIGTERM signal
fmt.Println("\n\nPress Crtl+C to shutdown nodes")
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
<-ch
fmt.Println("\nReceived signal, shutting down...")
stopNodes(mailservers)
stopNodes(whisperNodes)
}
func startBootnode() (string, error) {
envVars := os.Environ()
envVars = append(envVars, "CONTAINER_NAME=test-bootnode")
envVars = append(envVars, "COMPOSE_UP_FLAGS=-f ./docker-compose.yml -f ../../../../../scripts/localfleet/docker-compose-host.yml")
e := exec.Command("make", "-C", "../../vendor/status-go", "run-bootnode-docker")
var stderr bytes.Buffer
e.Stderr = &stderr
e.Env = envVars
if err := e.Run(); err != nil {
return "", fmt.Errorf("could not start bootnode: %w, %s", err, stderr.String())
}
e = exec.Command("make", "-s", "-C", "../../vendor/status-go/_assets/compose/bootnode", "enode")
e.Env = envVars
var out bytes.Buffer
e.Stdout = &out
err := e.Run()
if err != nil {
return "", fmt.Errorf("could not obtain bootnode enr: %w", err)
}
return strings.TrimSpace(out.String()), nil
}
func stopBootnode() {
fmt.Println("Stopping bootnode...")
envVars := os.Environ()
envVars = append(envVars, "CONTAINER_NAME=test-bootnode")
e := exec.Command("make", "-C", "../../vendor/status-go/_assets/compose/bootnode", "stop")
e.Env = envVars
err := e.Run()
if err != nil {
fmt.Println(fmt.Errorf("could not stop bootnode: %w", err))
}
}
func startNode(i int, bootnodeENR string, mailserver bool) (Node, error) {
envVars := os.Environ()
name := fmt.Sprintf("%s-%d", "test-mailserver", i)
if !mailserver {
name = fmt.Sprintf("%s-%d", "test-whisper", i)
envVars = append(envVars, "MAILSERVER_ENABLED=false")
}
rpcPort := 8656 + i
envVars = append(envVars, fmt.Sprintf("RPC_HOST=%s", "0.0.0.0"))
envVars = append(envVars, fmt.Sprintf("LISTEN_PORT=%d", 30310+i))
envVars = append(envVars, fmt.Sprintf("METRICS_PORT=%d", 9191+i))
envVars = append(envVars, fmt.Sprintf("RPC_PORT=%d", rpcPort))
envVars = append(envVars, fmt.Sprintf("CONTAINER_NAME=%s", name))
envVars = append(envVars, fmt.Sprintf("DATA_PATH=/var/tmp/%s", name))
envVars = append(envVars, fmt.Sprintf("BOOTNODE=%s", strings.TrimSpace(bootnodeENR)))
envVars = append(envVars, "CONTAINER_IMG=statusteam/status-go")
envVars = append(envVars, "LOG_LEVEL=DEBUG")
envVars = append(envVars, "CONTAINER_TAG=v0.84.0")
envVars = append(envVars, "API_MODULES=eth,web3,admin,waku,wakuext")
envVars = append(envVars, "REGISTER_TOPIC=whispermail")
envVars = append(envVars, "MAIL_PASSWORD=status-offline-inbox")
e := exec.Command("./gen-config.sh")
e.Env = envVars
if err := e.Run(); err != nil {
return Node{}, fmt.Errorf("could not generate config: %w", err)
}
e = exec.Command("docker-compose", "-p", name, "-f", "../../vendor/status-go/_assets/compose/mailserver/docker-compose.yml", "-f", "./docker-compose-host.yml", "up", "-d")
e.Env = envVars
var stderr bytes.Buffer
e.Stderr = &stderr
if err := e.Run(); err != nil {
return Node{}, fmt.Errorf("could not start mailserver: %w", errors.New(stderr.String()))
}
e = exec.Command("make", "-s", "-C", "../../vendor/status-go/_assets/compose/mailserver", "enode")
e.Env = envVars
var out bytes.Buffer
e.Stdout = &out
err := e.Run()
if err != nil {
return Node{}, fmt.Errorf("could not obtain mailserver #%d enr: %w", i, err)
}
return Node{
enr: strings.Replace(strings.TrimSpace(out.String()), ":status-offline-inbox", "", -1),
name: name,
rpcPort: 8656 + i,
}, nil
}
func stopNodes(nodes []Node) {
for _, node := range nodes {
fmt.Println(fmt.Sprintf("Stopping node %s...", node.name))
envVars := os.Environ()
envVars = append(envVars, fmt.Sprintf("CONTAINER_NAME=%s", node.name))
e := exec.Command("docker-compose", "-p", node.name, "down")
e.Env = envVars
if err := e.Run(); err != nil {
fmt.Println(fmt.Errorf("could not stop node #%s: %w", node.name, err))
}
}
}
func addPeer(peerENR string, port int) {
envVars := os.Environ()
envVars = append(envVars, fmt.Sprintf("RPC_HOST=%s", "0.0.0.0"))
envVars = append(envVars, fmt.Sprintf("RPC_PORT=%d", port))
e := exec.Command("../../vendor/status-go/_assets/scripts/rpc.sh", "admin_addPeer", peerENR)
e.Env = envVars
var out bytes.Buffer
e.Stdout = &out
err := e.Run()
if err != nil {
fmt.Println("could not add peer: ", err)
}
}