beehive/worker.go

244 lines
4.8 KiB
Go
Raw Normal View History

2023-04-08 03:46:22 +00:00
package beehive
2023-04-10 04:03:48 +00:00
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"log"
2023-05-03 05:36:43 +00:00
"net"
"os"
"path"
2023-04-10 04:03:48 +00:00
"strconv"
"time"
2023-04-10 04:03:48 +00:00
)
2023-05-03 05:36:43 +00:00
const retryDelay = time.Second * 2
2023-04-08 03:46:22 +00:00
2023-05-03 05:36:43 +00:00
type WorkerConfig struct {
// Path to festoons.
FestoonsDir string
2023-04-08 03:46:22 +00:00
2023-05-03 05:36:43 +00:00
// Path to deployments.
2023-04-10 04:03:48 +00:00
DeploymentsDir string
2023-04-08 03:46:22 +00:00
2023-05-03 05:36:43 +00:00
// Address to connect to queen bee.
Queen string
// Worker IP address.
IP string
// Unique ID.
ID int
// Password.
Password string
}
type Worker struct {
WorkerConfig
2023-04-08 03:46:22 +00:00
Deployments []*Deployment
2023-04-10 04:03:48 +00:00
2023-04-27 02:04:37 +00:00
TaskQueue chan *TaskMessage
2023-04-10 04:03:48 +00:00
requestPortsFunc func(d *Deployment) []int
2023-05-03 05:36:43 +00:00
reconnect chan struct{}
2023-04-10 04:03:48 +00:00
}
2023-05-03 05:36:43 +00:00
func NewWorker(config *WorkerConfig) *Worker {
2023-04-10 04:03:48 +00:00
w := &Worker{
2023-05-03 05:36:43 +00:00
WorkerConfig: *config,
TaskQueue: make(chan *TaskMessage),
reconnect: make(chan struct{}),
2023-04-10 04:03:48 +00:00
}
go w.handleTaskQueue()
log.Println("Loading deployments...")
// TODO list directories in deployments dir, no db needed
2023-05-03 05:36:43 +00:00
dirEntries, err := os.ReadDir(config.DeploymentsDir)
if err != nil {
log.Fatal(err)
}
for _, dirEntry := range dirEntries {
if dirEntry.IsDir() {
log.Println(dirEntry.Name())
2023-05-03 05:36:43 +00:00
d, err := LoadDeployment(path.Join(config.DeploymentsDir, dirEntry.Name()))
if err != nil {
log.Fatalf("failed to load deployment %s: %s", dirEntry.Name(), err)
}
d.Worker = w
w.Deployments = append(w.Deployments, d)
}
}
log.Println("Finished loading deployments")
2023-05-03 05:36:43 +00:00
go w.handleConnect()
2023-04-10 04:03:48 +00:00
return w
}
2023-05-03 05:36:43 +00:00
func (w *Worker) handleConnect() {
log.Printf("Connecting to queen bee at %s...", w.Queen)
for {
var conn net.Conn
var err error
for {
conn, err = net.Dial("tcp", w.Queen)
if err != nil {
log.Printf("Failed to connect to queen: %s", err)
log.Println("Retrying in 2 seconds...")
time.Sleep(retryDelay)
continue
}
break
}
log.Println("Connected")
client := NewClient(conn)
client.Authenticate(w.ID, w.Password)
go w.HandleRead(client)
<-w.reconnect
log.Println("Reconnecting...")
}
}
2023-04-10 04:03:48 +00:00
func (w *Worker) handleTaskQueue() {
for t := range w.TaskQueue {
w.ExecuteTask(t)
}
}
2023-04-27 02:04:37 +00:00
func (w *Worker) ExecuteTask(t *TaskMessage) error {
2023-04-10 04:03:48 +00:00
return nil
}
func (w *Worker) HandleRead(c *Client) {
var readFirst bool
scanner := bufio.NewScanner(c.Conn)
for scanner.Scan() {
2023-04-15 05:20:08 +00:00
log.Printf(" <- %s", scanner.Bytes())
2023-04-10 04:03:48 +00:00
if !readFirst {
if !bytes.Equal(scanner.Bytes(), []byte(serverWelcomeMessage)) {
log.Fatalf("unexpected server reply: %s", scanner.Bytes())
}
readFirst = true
continue
}
2023-04-27 02:04:37 +00:00
task := &TaskMessage{}
2023-04-10 04:03:48 +00:00
err := json.Unmarshal(scanner.Bytes(), task)
if err != nil {
log.Fatalf("failed to unmarshal %s: %s", scanner.Bytes(), err)
}
log.Printf(" <- task: %+v", task)
switch task.Type {
case TaskHealth:
2023-04-15 05:20:08 +00:00
result := NewResult(TaskHealth, map[string]string{
2023-04-10 04:03:48 +00:00
"time": task.Parameters["time"],
})
2023-04-15 05:20:08 +00:00
for _, d := range w.Deployments {
eventsJson, err := json.Marshal(d.Events)
2023-04-15 05:20:08 +00:00
if err != nil {
log.Fatal(err)
2023-04-15 05:20:08 +00:00
}
result.Parameters[fmt.Sprintf("events_%d", d.ID)] = string(eventsJson)
d.Events = d.Events[:0]
// TODO deployment mutex
2023-04-15 05:20:08 +00:00
}
2023-04-10 04:03:48 +00:00
resultJson, err := json.Marshal(result)
if err != nil {
log.Fatalf("failed to marshal result %+v: %s", result, err)
}
c.Out <- append(resultJson, '\n')
case TaskDeploy:
id, err := strconv.Atoi(task.Parameters["id"])
if err != nil {
log.Fatalf("failed to parse id: %s", err)
}
client, err := strconv.Atoi(task.Parameters["client"])
if err != nil {
log.Fatalf("failed to parse client: %s", err)
}
// TODO
ports := make([]int, 10)
for i := range ports {
ports[i], err = strconv.Atoi(task.Parameters[fmt.Sprintf("port_%d", i)])
if err != nil {
log.Fatalf("failed to parse port %d: %s", i, err)
}
}
var d *Deployment
var newDeployment bool
for _, deployment := range w.Deployments {
if id == deployment.ID {
d = deployment
d.Client = client
d.Festoon = task.Parameters["festoon"]
d.Ports = ports
break
}
2023-04-10 04:03:48 +00:00
}
if d == nil {
d = &Deployment{
ID: id,
Client: client,
Festoon: task.Parameters["festoon"],
UID: 7777,
Ports: ports,
Worker: w,
}
2023-04-10 04:03:48 +00:00
newDeployment = true
go d.handleEvents()
time.Sleep(10 * time.Millisecond) // Give events handler some time to attach.
}
2023-04-10 04:03:48 +00:00
err = d.deploy()
if err != nil {
log.Fatalf("failed to deploy %+v: %s", d, err)
}
if newDeployment {
w.Deployments = append(w.Deployments, d)
}
2023-04-15 05:20:08 +00:00
2023-04-10 04:03:48 +00:00
// Send result
2023-04-15 05:20:08 +00:00
result := NewResult(TaskDeploy, map[string]string{
2023-04-10 04:03:48 +00:00
"id": task.Parameters["id"],
"status": "ok",
})
resultJson, err := json.Marshal(result)
if err != nil {
log.Fatalf("failed to marshal result %+v: %s", result, err)
}
c.Out <- append(resultJson, '\n')
default:
log.Fatalf("unknown task type %d", task.Type)
}
}
2023-05-03 05:36:43 +00:00
log.Println("Connection lost")
w.reconnect <- struct{}{}
2023-04-08 03:46:22 +00:00
}