mirror/merlin/arthur/arthur.go

145 lines
3.9 KiB
Go
Raw Normal View History

2021-12-11 18:28:09 -05:00
package arthur
import (
"errors"
"fmt"
2021-12-15 01:18:52 -05:00
"io/ioutil"
2021-12-11 18:28:09 -05:00
"net"
"os"
"path/filepath"
2021-12-15 01:18:52 -05:00
"sort"
2021-12-11 18:28:09 -05:00
"strings"
"text/tabwriter"
"time"
"git.csclub.uwaterloo.ca/public/merlin/config"
"git.csclub.uwaterloo.ca/public/merlin/logger"
"git.csclub.uwaterloo.ca/public/merlin/sync"
)
2021-12-15 01:18:52 -05:00
// Reads and parses the message sent over the accepted connection
func GetCommand(conn net.Conn) (command, repoName string) {
command = ""
repoName = ""
2021-12-11 18:28:09 -05:00
2021-12-15 01:18:52 -05:00
buf, err := ioutil.ReadAll(conn)
2021-12-11 18:28:09 -05:00
if err != nil {
logger.ErrLog(err.Error())
return
}
2021-12-15 01:18:52 -05:00
args := strings.Split(string(buf), ":")
if len(args) >= 1 {
command = args[0]
}
if len(args) >= 2 {
repoName = args[1]
2021-12-11 18:28:09 -05:00
}
2021-12-15 01:18:52 -05:00
return
}
2021-12-11 18:28:09 -05:00
2022-06-18 02:13:23 -04:00
// Receives an message and writes it to both stdout and the accepted connection
2021-12-15 01:18:52 -05:00
func SendAndLog(conn net.Conn, msg string) {
logger.OutLog(msg)
conn.Write([]byte(msg))
}
2021-12-11 18:28:09 -05:00
2022-06-18 02:13:23 -04:00
// Writes the status of the repos to the accepted connection
2021-12-15 01:18:52 -05:00
func SendStatus(conn net.Conn) {
2022-01-07 17:49:59 -05:00
// Force arthur to send back time information in America/Toronto time
location, err := time.LoadLocation("America/Toronto")
if err != nil {
logger.ErrLog(err)
}
2021-12-15 01:18:52 -05:00
status := tabwriter.NewWriter(conn, 5, 5, 5, ' ', 0)
fmt.Fprintf(status, "Repository\tLast Synced\tNext Expected Sync\tRunning\n")
2021-12-11 18:28:09 -05:00
2022-06-18 02:13:23 -04:00
// get the names of all of the repos in the config
var keys []string
2021-12-15 01:18:52 -05:00
for name, _ := range config.RepoMap {
keys = append(keys, name)
}
sort.Strings(keys)
2022-06-18 02:13:23 -04:00
// print out the state of each repo in the config (last and next sync time + if it is currently running)
2021-12-15 01:18:52 -05:00
// for other ways to format the time see: https://pkg.go.dev/time#pkg-constants
for _, name := range keys {
repo := config.RepoMap[name]
lastSync := repo.State.LastAttemptStartTime
nextSync := lastSync + int64(repo.Frequency)
fmt.Fprintf(status, "%s\t%s\t%s\t%t\n",
name,
2022-01-07 17:49:59 -05:00
time.Unix(lastSync, 0).In(location).Format(time.RFC1123),
time.Unix(nextSync, 0).In(location).Format(time.RFC1123),
2021-12-15 01:18:52 -05:00
repo.State.IsRunning,
)
}
status.Flush()
}
2022-06-18 02:13:23 -04:00
// Attempt to force the sync of the repo. Returns true iff a sync was started.
2021-12-15 01:18:52 -05:00
func ForceSync(conn net.Conn, repoName string) (newSync bool) {
newSync = false
if repo, isInMap := config.RepoMap[repoName]; isInMap {
logger.OutLog("Attempting to force sync of " + repoName)
2022-06-27 23:36:51 -04:00
if sync.SyncIfPossible(repo, true) {
2021-12-15 01:18:52 -05:00
conn.Write([]byte("Forced sync for " + repoName))
newSync = true
2021-12-11 18:28:09 -05:00
} else {
2021-12-15 01:18:52 -05:00
SendAndLog(conn, "Could not force sync: "+repoName+" is already syncing.")
2021-12-11 18:28:09 -05:00
}
} else {
2021-12-15 01:18:52 -05:00
SendAndLog(conn, repoName+" is not tracked so cannot sync")
2021-12-11 18:28:09 -05:00
}
return
}
2022-06-18 02:13:23 -04:00
// Create and start listening to a unix socket and accept and forward connections to the connChan channel
2021-12-15 01:18:52 -05:00
func StartListener(connChan chan net.Conn, stopLisChan chan struct{}) {
2021-12-11 18:28:09 -05:00
sockpath := config.Conf.SockPath
if filepath.Ext(sockpath) != ".sock" {
2021-12-15 01:18:52 -05:00
panic(fmt.Errorf("socket file must end with .sock"))
2022-06-18 02:13:23 -04:00
}
// Attempt to remove sockpath if exists, continue if does not exist.
// If old sockpath is not removed then will get "bind: address already in use".
if _, err := os.Stat(sockpath); err == nil { // sockpath exists
2021-12-11 18:28:09 -05:00
if err := os.Remove(sockpath); err != nil {
panic(err)
}
2022-06-18 02:13:23 -04:00
} else if !errors.Is(err, os.ErrNotExist) { // error is not that the sockpath does not exist
2021-12-11 18:28:09 -05:00
panic(err)
}
2022-06-18 02:13:23 -04:00
// creates and starts listening to a unix socket
2021-12-11 18:28:09 -05:00
ear, err := net.Listen("unix", sockpath)
if err != nil {
panic(err)
}
logger.OutLog("Listening to unix socket at " + sockpath)
go func() {
for {
2022-06-18 02:13:23 -04:00
// Note: cannot use select with ear.Accept() for some reason (it does block until connection is made)
2021-12-11 18:28:09 -05:00
conn, err := ear.Accept()
if err != nil {
2022-06-18 02:13:23 -04:00
// attempting to accept on a closed net.Listener will return a non-temporary error
2021-12-15 01:18:52 -05:00
if netErr, isTemp := err.(net.Error); isTemp && netErr.Temporary() {
2021-12-11 18:28:09 -05:00
logger.ErrLog("Accepted socket error: " + err.Error())
continue
}
logger.ErrLog("Unhandlable socket error: " + err.Error())
return
}
connChan <- conn
}
}()
2022-06-18 02:13:23 -04:00
// wait for stopLisChan to close or signal to close the socket listener
2021-12-11 18:28:09 -05:00
<-stopLisChan
ear.Close()
}