mirror of
https://github.com/postmannen/ctrl.git
synced 2024-12-14 12:37:31 +00:00
176 lines
4.6 KiB
Go
176 lines
4.6 KiB
Go
package steward
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
|
|
"github.com/fsnotify/fsnotify"
|
|
)
|
|
|
|
// getMessagesFromFile will start a file watcher for the given directory
|
|
// and filename. It will take a channel of []byte as input, and it is
|
|
// in this channel the content of a file that has changed is returned.
|
|
func (s *server) getMessagesFromFile(directoryToCheck string, fileName string, inputFromFileCh chan []subjectAndMessage) {
|
|
fileUpdated := make(chan bool)
|
|
go fileWatcherStart(directoryToCheck, fileUpdated)
|
|
|
|
for range fileUpdated {
|
|
|
|
//load file, read it's content
|
|
b, err := readTruncateMessageFile(fileName)
|
|
if err != nil {
|
|
log.Printf("error: reading file: %v", err)
|
|
}
|
|
|
|
// Start on top again if the file did not contain
|
|
// any data.
|
|
if len(b) == 0 {
|
|
continue
|
|
}
|
|
|
|
// unmarshal the JSON into a struct
|
|
js, err := jsonFromFileData(b)
|
|
if err != nil {
|
|
er := fmt.Errorf("error: malformed json: %v", err)
|
|
log.Printf("%v\n", er)
|
|
sendErrorLogMessage(s.newMessagesCh, node(s.nodeName), er)
|
|
continue
|
|
}
|
|
|
|
for i := range js {
|
|
fmt.Printf("*** Checking message found in file: messageType type: %T, messagetype contains: %#v\n", js[i].Subject.CommandOrEvent, js[i].Subject.CommandOrEvent)
|
|
// Fill in the value for the FromNode field, so the receiver
|
|
// can check this field to know where it came from.
|
|
js[i].Message.FromNode = node(s.nodeName)
|
|
}
|
|
|
|
// Send the data back to be consumed
|
|
inputFromFileCh <- js
|
|
}
|
|
er := fmt.Errorf("error: getMessagesFromFile stopped")
|
|
log.Printf("%v\n", er)
|
|
sendErrorLogMessage(s.newMessagesCh, node(s.nodeName), er)
|
|
}
|
|
|
|
type subjectAndMessage struct {
|
|
Subject `json:"subject" yaml:"subject"`
|
|
Message `json:"message" yaml:"message"`
|
|
}
|
|
|
|
// jsonFromFileData will range over the message given in json format. For
|
|
// each element found the Message type will be converted into a SubjectAndMessage
|
|
// type value and appended to a slice, and the slice is returned to the caller.
|
|
func jsonFromFileData(b []byte) ([]subjectAndMessage, error) {
|
|
MsgSlice := []Message{}
|
|
|
|
err := json.Unmarshal(b, &MsgSlice)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error: unmarshal of file failed: %#v", err)
|
|
}
|
|
|
|
sam := []subjectAndMessage{}
|
|
|
|
// Range over all the messages parsed from json, and create a subject for
|
|
// each message.
|
|
for _, m := range MsgSlice {
|
|
sm := newSAM(m)
|
|
sam = append(sam, sm)
|
|
}
|
|
|
|
return sam, nil
|
|
}
|
|
|
|
// newSAM will look up the correct values and value types to
|
|
// be used in a subject for a Message, and return the a combined structure
|
|
// of type subjectAndMessage.
|
|
func newSAM(m Message) subjectAndMessage {
|
|
// We need to create a tempory method type to look up the kind for the
|
|
// real method for the message.
|
|
var mt Method
|
|
|
|
sub := Subject{
|
|
ToNode: string(m.ToNode),
|
|
CommandOrEvent: mt.getHandler(m.Method).getKind(),
|
|
Method: m.Method,
|
|
}
|
|
|
|
sm := subjectAndMessage{
|
|
Subject: sub,
|
|
Message: m,
|
|
}
|
|
|
|
return sm
|
|
}
|
|
|
|
// readTruncateMessageFile, will read all the messages in the given
|
|
// file, and truncate the file after read.
|
|
// A []byte will be returned with the content read.
|
|
func readTruncateMessageFile(fileName string) ([]byte, error) {
|
|
|
|
f, err := os.OpenFile(fileName, os.O_APPEND|os.O_RDWR, os.ModeAppend)
|
|
if err != nil {
|
|
log.Printf("error: readTruncateMessageFile: Failed to open file: %v\n", err)
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
|
|
scanner := bufio.NewScanner(f)
|
|
|
|
lines := []byte{}
|
|
|
|
for scanner.Scan() {
|
|
lines = append(lines, scanner.Bytes()...)
|
|
}
|
|
|
|
// empty the file after all is read
|
|
_, err = f.Seek(0, io.SeekStart)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("f.Seek failed: %v", err)
|
|
}
|
|
|
|
err = f.Truncate(0)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("f.Truncate failed: %v", err)
|
|
}
|
|
|
|
return lines, nil
|
|
}
|
|
|
|
// Start the file watcher that will check if the in pipe for new operator
|
|
// messages are updated with new content.
|
|
func fileWatcherStart(directoryToCheck string, fileUpdated chan bool) {
|
|
watcher, err := fsnotify.NewWatcher()
|
|
if err != nil {
|
|
log.Println("Failed fsnotify.NewWatcher")
|
|
return
|
|
}
|
|
defer watcher.Close()
|
|
|
|
done := make(chan bool)
|
|
go func() {
|
|
//Give a true value to updated so it reads the file the first time.
|
|
fileUpdated <- true
|
|
for {
|
|
select {
|
|
case event := <-watcher.Events:
|
|
if event.Op&fsnotify.Write == fsnotify.Write {
|
|
// log.Println("info: inmsg.txt file updated, processing input: ", event.Name)
|
|
//testing with an update chan to get updates
|
|
fileUpdated <- true
|
|
}
|
|
case err := <-watcher.Errors:
|
|
log.Println("error:", err)
|
|
}
|
|
}
|
|
}()
|
|
|
|
err = watcher.Add(directoryToCheck)
|
|
if err != nil {
|
|
log.Printf("error: watcher add: %v\n", err)
|
|
}
|
|
<-done
|
|
}
|