2021-01-28 10:17:54 +00:00
// Notes:
2021-02-01 10:13:38 +00:00
package steward
2021-01-25 14:23:00 +00:00
import (
"fmt"
"log"
2021-03-02 12:46:02 +00:00
"os"
2021-01-28 13:58:16 +00:00
"sync"
2021-01-25 14:23:00 +00:00
"time"
"github.com/nats-io/nats.go"
2021-02-19 15:58:16 +00:00
"github.com/prometheus/client_golang/prometheus"
2021-01-25 14:23:00 +00:00
)
2021-02-26 06:55:28 +00:00
type processName string
2021-03-09 03:55:51 +00:00
// Will return a process name made up of subjectName+processKind
2021-02-26 06:55:28 +00:00
func processNameGet ( sn subjectName , pk processKind ) processName {
pn := fmt . Sprintf ( "%s_%s" , sn , pk )
return processName ( pn )
}
2021-03-03 14:44:32 +00:00
// processes holds all the information about running processes
type processes struct {
// The active spawned processes
active map [ processName ] process
// mutex to lock the map
mu sync . Mutex
// The last processID created
lastProcessID int
}
// newProcesses will prepare and return a *processes
func newProcesses ( ) * processes {
p := processes {
active : make ( map [ processName ] process ) ,
}
return & p
}
2021-01-28 10:17:54 +00:00
// server is the structure that will hold the state about spawned
// processes on a local instance.
2021-01-27 13:02:57 +00:00
type server struct {
2021-03-01 19:49:43 +00:00
// Configuration options used for running the server
configuration * Configuration
// The nats connection to the broker
2021-01-27 13:02:57 +00:00
natsConn * nats . Conn
2021-03-03 14:44:32 +00:00
// processes holds all the information about running processes
processes * processes
2021-02-04 12:26:10 +00:00
// The name of the node
nodeName string
2021-02-24 09:58:02 +00:00
// Mutex for locking when writing to the process map
2021-02-24 14:43:31 +00:00
newMessagesCh chan [ ] subjectAndMessage
2021-02-24 09:58:02 +00:00
// errorKernel is doing all the error handling like what to do if
// an error occurs.
// TODO: Will also send error messages to cental error subscriber.
2021-02-05 12:56:42 +00:00
errorKernel * errorKernel
2021-02-18 11:29:14 +00:00
// metric exporter
metrics * metrics
2021-02-24 14:43:31 +00:00
// Is this the central error logger ?
2021-02-26 08:02:53 +00:00
// collection of the publisher services and the types to control them
publisherServices * publisherServices
2021-02-24 14:43:31 +00:00
centralErrorLogger bool
2021-01-27 13:02:57 +00:00
}
2021-01-28 10:17:54 +00:00
// newServer will prepare and return a server type
2021-03-01 19:49:43 +00:00
func NewServer ( c * Configuration ) ( * server , error ) {
conn , err := nats . Connect ( c . BrokerAddress , nil )
2021-02-01 10:13:38 +00:00
if err != nil {
log . Printf ( "error: nats.Connect failed: %v\n" , err )
}
2021-02-01 12:41:04 +00:00
s := & server {
2021-03-03 14:44:32 +00:00
configuration : c ,
nodeName : c . NodeName ,
natsConn : conn ,
processes : newProcesses ( ) ,
newMessagesCh : make ( chan [ ] subjectAndMessage ) ,
metrics : newMetrics ( c . PromHostAndPort ) ,
publisherServices : newPublisherServices ( c . PublisherServiceSayhello ) ,
centralErrorLogger : c . CentralErrorLogger ,
2021-02-01 12:41:04 +00:00
}
2021-01-29 05:09:48 +00:00
2021-03-02 12:46:02 +00:00
// Create the default data folder for where subscribers should
// write it's data if needed.
// Check if data folder exist, and create it if needed.
if _ , err := os . Stat ( c . SubscribersDataFolder ) ; os . IsNotExist ( err ) {
if c . SubscribersDataFolder == "" {
return nil , fmt . Errorf ( "error: subscribersDataFolder value is empty, you need to provide the config or the flag value at startup %v: %v" , c . SubscribersDataFolder , err )
}
err := os . Mkdir ( c . SubscribersDataFolder , 0700 )
if err != nil {
return nil , fmt . Errorf ( "error: failed to create directory %v: %v" , c . SubscribersDataFolder , err )
}
log . Printf ( "info: Creating subscribers data folder at %v\n" , c . SubscribersDataFolder )
}
2021-02-05 06:25:12 +00:00
return s , nil
}
2021-02-24 09:58:02 +00:00
// Start will spawn up all the predefined subscriber processes.
2021-02-10 06:25:44 +00:00
// Spawning of publisher processes is done on the fly by checking
2021-02-24 09:58:02 +00:00
// if there is publisher process for a given message subject, and
// not exist it will spawn one.
2021-02-10 04:11:48 +00:00
func ( s * server ) Start ( ) {
2021-02-19 10:07:09 +00:00
// Start the error kernel that will do all the error handling
// not done within a process.
s . errorKernel = newErrorKernel ( )
2021-02-24 14:43:31 +00:00
s . errorKernel . startErrorKernel ( s . newMessagesCh )
2021-02-19 10:07:09 +00:00
2021-02-18 11:29:14 +00:00
// Start collecting the metrics
go s . startMetrics ( )
2021-02-05 09:47:07 +00:00
// Start the checking the input file for new messages from operator.
2021-02-24 14:43:31 +00:00
go s . getMessagesFromFile ( "./" , "inmsg.txt" , s . newMessagesCh )
2021-02-02 12:06:37 +00:00
2021-02-26 14:11:20 +00:00
// if enabled, start the sayHello I'm here service at the given interval
2021-02-26 08:02:53 +00:00
if s . publisherServices . sayHelloPublisher . interval != 0 {
go s . publisherServices . sayHelloPublisher . start ( s . newMessagesCh , node ( s . nodeName ) )
}
2021-02-24 09:58:02 +00:00
// Start up the predefined subscribers.
// TODO: What to subscribe on should be handled via flags, or config
// files.
s . subscribersStart ( )
2021-02-18 13:27:53 +00:00
2021-03-04 15:27:55 +00:00
time . Sleep ( time . Second * 1 )
2021-02-10 06:25:44 +00:00
s . printProcessesMap ( )
2021-02-24 09:58:02 +00:00
// Start the processing of new messaging from an input channel.
2021-02-24 14:43:31 +00:00
s . processNewMessages ( "./incommmingBuffer.db" , s . newMessagesCh )
2021-02-05 09:47:07 +00:00
select { }
}
2021-02-10 06:25:44 +00:00
func ( s * server ) printProcessesMap ( ) {
fmt . Println ( "--------------------------------------------------------------------------------------------" )
fmt . Printf ( "*** Output of processes map :\n" )
2021-03-03 14:44:32 +00:00
s . processes . mu . Lock ( )
for _ , v := range s . processes . active {
2021-03-04 05:53:03 +00:00
fmt . Printf ( "* proc - : id: %v, name: %v, allowed from: %v\n" , v . processID , v . subject . name ( ) , v . allowedReceivers )
2021-02-10 06:25:44 +00:00
}
2021-03-03 14:44:32 +00:00
s . processes . mu . Unlock ( )
2021-02-19 15:58:16 +00:00
s . metrics . metricsCh <- metricType {
metric : prometheus . NewGauge ( prometheus . GaugeOpts {
Name : "total_running_processes" ,
Help : "The current number of total running processes" ,
} ) ,
2021-03-03 14:44:32 +00:00
value : float64 ( len ( s . processes . active ) ) ,
2021-02-19 15:58:16 +00:00
}
2021-02-10 06:25:44 +00:00
fmt . Println ( "--------------------------------------------------------------------------------------------" )
}
2021-02-25 10:08:05 +00:00
// sendErrorMessage will put the error message directly on the channel that is
// read by the nats publishing functions.
func sendErrorLogMessage ( newMessagesCh chan <- [ ] subjectAndMessage , FromNode node , theError error ) {
2021-02-24 14:43:31 +00:00
// --- Testing
2021-02-25 10:08:05 +00:00
sam := createErrorMsgContent ( FromNode , theError )
newMessagesCh <- [ ] subjectAndMessage { sam }
}
2021-02-24 14:43:31 +00:00
2021-02-25 10:08:05 +00:00
// createErrorMsgContent will prepare a subject and message with the content
// of the error
func createErrorMsgContent ( FromNode node , theError error ) subjectAndMessage {
2021-02-24 14:43:31 +00:00
// TESTING: Creating an error message to send to errorCentral
fmt . Printf ( " --- Sending error message to central !!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" )
sam := subjectAndMessage {
Subject : Subject {
ToNode : "errorCentral" ,
CommandOrEvent : EventNACK ,
Method : ErrorLog ,
} ,
Message : Message {
2021-03-01 16:08:40 +00:00
ToNode : "errorCentral" ,
FromNode : FromNode ,
Data : [ ] string { theError . Error ( ) } ,
Method : ErrorLog ,
2021-02-24 14:43:31 +00:00
} ,
}
2021-02-25 10:08:05 +00:00
return sam
2021-02-24 14:43:31 +00:00
}